/** @file\r
+ Support routines for Mtftp.\r
\r
-Copyright (c) 2006 - 2007, Intel Corporation\r
-All rights reserved. This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution. The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-\r
-Module Name:\r
-\r
- Mtftp4Support.c\r
-\r
-Abstract:\r
-\r
- Support routines for Mtftp\r
-\r
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
\r
\r
/**\r
- Allocate a MTFTP4 block range, then init it to the\r
- range of [Start, End]\r
+ Allocate a MTFTP4 block range, then init it to the range of [Start, End]\r
\r
@param Start The start block number\r
@param End The last block number in the range\r
\r
- @return NULL if failed to allocate memory, otherwise the created block range.\r
+ @return Pointer to the created block range, NULL if failed to allocate memory.\r
\r
**/\r
MTFTP4_BLOCK_RANGE *\r
{\r
MTFTP4_BLOCK_RANGE *Range;\r
\r
- Range = AllocatePool (sizeof (MTFTP4_BLOCK_RANGE));\r
+ Range = AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE));\r
\r
if (Range == NULL) {\r
return NULL;\r
InitializeListHead (&Range->Link);\r
Range->Start = Start;\r
Range->End = End;\r
+ Range->Bound = End;\r
\r
return Range;\r
}\r
\r
\r
/**\r
- Initialize the block range for either RRQ or WRQ. RRQ and WRQ have\r
- different requirements for Start and End. For example, during start\r
- up, WRQ initializes its whole valid block range to [0, 0xffff]. This\r
- is bacause the server will send us a ACK0 to inform us to start the\r
- upload. When the client received ACK0, it will remove 0 from the range,\r
- get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
- without option negotiation, the server will directly send us the BLOCK1\r
- in response to the client's RRQ. When received BLOCK1, the client will\r
- remove it from the block range and send an ACK. It also works if there\r
- is option negotiation.\r
+ Initialize the block range for either RRQ or WRQ.\r
+\r
+ RRQ and WRQ have different requirements for Start and End.\r
+ For example, during start up, WRQ initializes its whole valid block range\r
+ to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us\r
+ to start the upload. When the client received ACK0, it will remove 0 from the\r
+ range, get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
+ without option negotiation, the server will directly send us the BLOCK1 in\r
+ response to the client's RRQ. When received BLOCK1, the client will remove\r
+ it from the block range and send an ACK. It also works if there is option\r
+ negotiation.\r
\r
@param Head The block range head to initialize\r
@param Start The Start block number.\r
\r
@param Head The block range head\r
\r
- @return -1: if the block range is empty. Otherwise the first valid block number.\r
+ @return The first valid block number, -1 if the block range is empty.\r
\r
**/\r
INTN\r
\r
\r
/**\r
- Set the last block number of the block range list. It will\r
- remove all the blocks after the Last. MTFTP initialize the\r
- block range to the maximum possible range, such as [0, 0xffff]\r
- for WRQ. When it gets the last block number, it will call\r
- this function to set the last block number.\r
+ Set the last block number of the block range list.\r
+\r
+ It will remove all the blocks after the Last. MTFTP initialize the block range\r
+ to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the\r
+ last block number, it will call this function to set the last block number.\r
\r
@param Head The block range list\r
@param Last The last block number\r
\r
- @return None\r
-\r
**/\r
VOID\r
Mtftp4SetLastBlockNum (\r
\r
if (Range->Start > Last) {\r
RemoveEntryList (&Range->Link);\r
- gBS->FreePool (Range);\r
+ FreePool (Range);\r
continue;\r
}\r
\r
\r
@param Head The block range list to remove from\r
@param Num The block number to remove\r
+ @param Completed Whether Num is the last block number.\r
+ @param BlockCounter The continuous block counter instead of the value after roll-over.\r
\r
@retval EFI_NOT_FOUND The block number isn't in the block range list\r
@retval EFI_SUCCESS The block number has been removed from the list\r
EFI_STATUS\r
Mtftp4RemoveBlockNum (\r
IN LIST_ENTRY *Head,\r
- IN UINT16 Num\r
+ IN UINT16 Num,\r
+ IN BOOLEAN Completed,\r
+ OUT UINT64 *BlockCounter\r
)\r
{\r
MTFTP4_BLOCK_RANGE *Range;\r
} else if (Range->Start == Num) {\r
Range->Start++;\r
\r
- if (Range->Start > Range->End) {\r
+ //\r
+ // Note that: RFC 1350 does not mention block counter roll-over,\r
+ // but several TFTP hosts implement the roll-over be able to accept\r
+ // transfers of unlimited size. There is no consensus, however, whether\r
+ // the counter should wrap around to zero or to one. Many implementations\r
+ // wrap to zero, because this is the simplest to implement. Here we choose\r
+ // this solution.\r
+ //\r
+ *BlockCounter = Num;\r
+\r
+ if (Range->Round > 0) {\r
+ *BlockCounter += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;\r
+ }\r
+\r
+ if (Range->Start > Range->Bound) {\r
+ Range->Start = 0;\r
+ Range->Round ++;\r
+ }\r
+\r
+ if ((Range->Start > Range->End) || Completed) {\r
RemoveEntryList (&Range->Link);\r
- gBS->FreePool (Range);\r
+ FreePool (Range);\r
}\r
\r
return EFI_SUCCESS;\r
EFI_MTFTP4_PACKET *Packet;\r
EFI_MTFTP4_OPTION *Options;\r
EFI_MTFTP4_TOKEN *Token;\r
+ RETURN_STATUS Status;\r
NET_BUF *Nbuf;\r
UINT8 *Mode;\r
UINT8 *Cur;\r
- UINT32 Len;\r
UINTN Index;\r
- UINT32 Len1;\r
- UINT32 Len2;\r
+ UINT32 BufferLength;\r
+ UINTN FileNameLength;\r
+ UINTN ModeLength;\r
+ UINTN OptionStrLength;\r
+ UINTN ValueStrLength;\r
\r
Token = Instance->Token;\r
Options = Token->OptionList;\r
//\r
// Compute the packet length\r
//\r
- Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Token->Filename);\r
- Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Mode);\r
- Len = (Len1 + Len2 + 4);\r
+ FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);\r
+ ModeLength = AsciiStrLen ((CHAR8 *) Mode);\r
+ BufferLength = (UINT32) FileNameLength + (UINT32) ModeLength + 4;\r
\r
for (Index = 0; Index < Token->OptionCount; Index++) {\r
- Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
- Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
- Len += Len1 + Len2 + 2;\r
+ OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
+ ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
+ BufferLength += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;\r
}\r
-\r
//\r
// Allocate a packet then copy the data over\r
//\r
- if ((Nbuf = NetbufAlloc (Len)) == NULL) {\r
+ if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
}\r
\r
- Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);\r
+ Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);\r
+ ASSERT (Packet != NULL);\r
+\r
Packet->OpCode = HTONS (Instance->Operation);\r
+ BufferLength -= sizeof (Packet->OpCode);\r
+\r
Cur = Packet->Rrq.Filename;\r
- Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Token->Filename);\r
- Cur += AsciiStrLen ((CHAR8 *) Token->Filename) + 1;\r
- Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Mode);\r
- Cur += AsciiStrLen ((CHAR8 *) Mode) + 1;\r
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);\r
+ ASSERT_EFI_ERROR (Status);\r
+ BufferLength -= (UINT32) (FileNameLength + 1);\r
+ Cur += FileNameLength + 1;\r
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);\r
+ ASSERT_EFI_ERROR (Status);\r
+ BufferLength -= (UINT32) (ModeLength + 1);\r
+ Cur += ModeLength + 1;\r
\r
for (Index = 0; Index < Token->OptionCount; ++Index) {\r
- Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].OptionStr);\r
- Cur += AsciiStrLen ((CHAR8 *) Options[Index].OptionStr) + 1;\r
+ OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
+ ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
+\r
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);\r
+ ASSERT_EFI_ERROR (Status);\r
+ BufferLength -= (UINT32) (OptionStrLength + 1);\r
+ Cur += OptionStrLength + 1;\r
+\r
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);\r
+ ASSERT_EFI_ERROR (Status);\r
+ BufferLength -= (UINT32) (ValueStrLength + 1);\r
+ Cur += ValueStrLength + 1;\r
\r
- Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].ValueStr);\r
- Cur += AsciiStrLen ((CHAR8 *) (CHAR8 *) Options[Index].ValueStr) + 1;\r
}\r
\r
return Mtftp4SendPacket (Instance, Nbuf);\r
\r
\r
/**\r
- Build then send an error message\r
+ Build then send an error message.\r
\r
@param Instance The MTFTP session\r
- @param ErrInfo The error code and message\r
+ @param ErrCode The error code\r
+ @param ErrInfo The error message\r
\r
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet\r
@retval EFI_SUCCESS The error packet is transmitted.\r
Mtftp4SendError (\r
IN MTFTP4_PROTOCOL *Instance,\r
IN UINT16 ErrCode,\r
- IN UINT8* ErrInfo\r
+ IN UINT8 *ErrInfo\r
)\r
{\r
NET_BUF *Packet;\r
\r
Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));\r
Packet = NetbufAlloc (Len);\r
-\r
if (Packet == NULL) {\r
return EFI_OUT_OF_RESOURCES;\r
}\r
\r
TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);\r
+ ASSERT (TftpError != NULL);\r
+\r
TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);\r
TftpError->Error.ErrorCode = HTONS (ErrCode);\r
\r
- AsciiStrCpy ((CHAR8 *) TftpError->Error.ErrorMessage, (CHAR8 *) ErrInfo);\r
+ AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, Len, (CHAR8 *) ErrInfo);\r
\r
return Mtftp4SendPacket (Instance, Packet);\r
}\r
\r
/**\r
The callback function called when the packet is transmitted.\r
+\r
It simply frees the packet.\r
\r
@param Packet The transmitted (or failed to) packet\r
- @param Points The local and remote UDP access point\r
+ @param EndPoint The local and remote UDP access point\r
@param IoStatus The result of the transmission\r
@param Context Opaque parameter to the callback\r
\r
- @return None\r
-\r
**/\r
VOID\r
+EFIAPI\r
Mtftp4OnPacketSent (\r
- NET_BUF *Packet,\r
- UDP_POINTS *Points,\r
- EFI_STATUS IoStatus,\r
- VOID *Context\r
+ IN NET_BUF *Packet,\r
+ IN UDP_END_POINT *EndPoint,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
)\r
{\r
NetbufFree (Packet);\r
\r
\r
/**\r
- Set the timeout for the instance. User a longer time for\r
- passive instances.\r
+ Set the timeout for the instance. User a longer time for passive instances.\r
\r
@param Instance The Mtftp session to set time out\r
\r
- @return None\r
-\r
**/\r
VOID\r
Mtftp4SetTimeout (\r
- IN MTFTP4_PROTOCOL *Instance\r
+ IN OUT MTFTP4_PROTOCOL *Instance\r
)\r
{\r
if (Instance->Master) {\r
\r
\r
/**\r
- Send the packet for the instance. It will first save a reference to\r
- the packet for later retransmission. then determine the destination\r
- port, listen port for requests, and connected port for others. At last,\r
- send the packet out.\r
+ Send the packet for the instance.\r
+\r
+ It will first save a reference to the packet for later retransmission.\r
+ Then determine the destination port, listen port for requests, and connected\r
+ port for others. At last, send the packet out.\r
\r
@param Instance The Mtftp instance\r
@param Packet The packet to send\r
**/\r
EFI_STATUS\r
Mtftp4SendPacket (\r
- IN MTFTP4_PROTOCOL *Instance,\r
- IN NET_BUF *Packet\r
+ IN OUT MTFTP4_PROTOCOL *Instance,\r
+ IN OUT NET_BUF *Packet\r
)\r
{\r
- UDP_POINTS UdpPoint;\r
+ UDP_END_POINT UdpPoint;\r
EFI_STATUS Status;\r
UINT16 OpCode;\r
- UINT16 Value;\r
+ UINT8 *Buffer;\r
\r
//\r
// Save the packet for retransmission\r
NetbufFree (Instance->LastPacket);\r
}\r
\r
- Instance->LastPacket = Packet;\r
+ Instance->LastPacket = Packet;\r
\r
- Instance->CurRetry = 0;\r
+ Instance->CurRetry = 0;\r
Mtftp4SetTimeout (Instance);\r
\r
- UdpPoint.LocalAddr = 0;\r
- UdpPoint.LocalPort = 0;\r
- UdpPoint.RemoteAddr = Instance->ServerIp;\r
+ ZeroMem (&UdpPoint, sizeof (UdpPoint));\r
+ UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;\r
\r
//\r
// Send the requests to the listening port, other packets\r
// to the connected port\r
//\r
- Value = *((UINT16 *) NetbufGetByte (Packet, 0, NULL));\r
- OpCode = NTOHS (Value);\r
+ Buffer = NetbufGetByte (Packet, 0, NULL);\r
+ ASSERT (Buffer != NULL);\r
+ OpCode = NTOHS (*(UINT16 *)Buffer);\r
\r
- if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||\r
+ if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) ||\r
+ (OpCode == EFI_MTFTP4_OPCODE_DIR) ||\r
(OpCode == EFI_MTFTP4_OPCODE_WRQ)) {\r
UdpPoint.RemotePort = Instance->ListeningPort;\r
} else {\r
Instance->UnicastPort,\r
Packet,\r
&UdpPoint,\r
- 0,\r
+ NULL,\r
Mtftp4OnPacketSent,\r
Instance\r
);\r
\r
\r
/**\r
- Retransmit the last packet for the instance\r
+ Retransmit the last packet for the instance.\r
\r
@param Instance The Mtftp instance\r
\r
IN MTFTP4_PROTOCOL *Instance\r
)\r
{\r
- UDP_POINTS UdpPoint;\r
+ UDP_END_POINT UdpPoint;\r
EFI_STATUS Status;\r
UINT16 OpCode;\r
- UINT16 Value;\r
+ UINT8 *Buffer;\r
\r
ASSERT (Instance->LastPacket != NULL);\r
\r
- UdpPoint.LocalAddr = 0;\r
- UdpPoint.LocalPort = 0;\r
- UdpPoint.RemoteAddr = Instance->ServerIp;\r
+ ZeroMem (&UdpPoint, sizeof (UdpPoint));\r
+ UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;\r
\r
//\r
// Set the requests to the listening port, other packets to the connected port\r
//\r
- Value = *(UINT16 *) NetbufGetByte (Instance->LastPacket, 0, NULL);\r
- OpCode = NTOHS (Value);\r
+ Buffer = NetbufGetByte (Instance->LastPacket, 0, NULL);\r
+ ASSERT (Buffer != NULL);\r
+ OpCode = NTOHS (*(UINT16 *) Buffer);\r
\r
if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||\r
(OpCode == EFI_MTFTP4_OPCODE_WRQ)) {\r
Instance->UnicastPort,\r
Instance->LastPacket,\r
&UdpPoint,\r
- 0,\r
+ NULL,\r
Mtftp4OnPacketSent,\r
Instance\r
);\r
\r
\r
/**\r
- The timer ticking function for the Mtftp service instance.\r
+ The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.\r
\r
@param Event The ticking event\r
@param Context The Mtftp service instance\r
\r
- @return None\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp4OnTimerTickNotifyLevel (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ MTFTP4_SERVICE *MtftpSb;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ MTFTP4_PROTOCOL *Instance;\r
+\r
+ MtftpSb = (MTFTP4_SERVICE *) Context;\r
+\r
+ //\r
+ // Iterate through all the children of the Mtftp service instance. Time\r
+ // out the current packet transmit.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {\r
+ Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);\r
+ if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {\r
+ Instance->HasTimeout = FALSE;\r
+ } else {\r
+ Instance->HasTimeout = TRUE;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ The timer ticking function for the Mtftp service instance.\r
+\r
+ @param Event The ticking event\r
+ @param Context The Mtftp service instance\r
\r
**/\r
VOID\r
MtftpSb = (MTFTP4_SERVICE *) Context;\r
\r
//\r
- // Iterate through all the children of the Mtftp service instance. Time\r
- // out the packet. If maximum retries reached, clean the session up.\r
+ // Iterate through all the children of the Mtftp service instance.\r
//\r
NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {\r
Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);\r
-\r
- if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {\r
+ if (!Instance->HasTimeout) {\r
continue;\r
}\r
\r
+ Instance->HasTimeout = FALSE;\r
+\r
//\r
// Call the user's time out handler\r
//\r
Token = Instance->Token;\r
\r
- if ((Token->TimeoutCallback != NULL) &&\r
+ if (Token != NULL && Token->TimeoutCallback != NULL &&\r
EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {\r
-\r
Mtftp4SendError (\r
- Instance,\r
- EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
- (UINT8 *) "User aborted the transfer in time out"\r
- );\r
+ Instance,\r
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
+ (UINT8 *) "User aborted the transfer in time out"\r
+ );\r
\r
Mtftp4CleanOperation (Instance, EFI_ABORTED);\r
continue;\r