diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c new file mode 100644 index 00000000..48e60f65 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c @@ -0,0 +1,598 @@ +/** @file + Mtftp6 Wrq process functions implementation. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Mtftp6Impl.h" + + + +/** + Build and send a Mtftp6 data packet for upload. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] BlockNum The block num to be sent. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet. + @retval EFI_SUCCESS The data packet was sent. + @retval EFI_ABORTED The user aborted this process. + +**/ +EFI_STATUS +Mtftp6WrqSendBlock ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 BlockNum + ) +{ + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_TOKEN *Token; + NET_BUF *UdpPacket; + EFI_STATUS Status; + UINT16 DataLen; + UINT8 *DataBuf; + UINT64 Start; + + // + // Allocate net buffer to create data packet. + // + UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN); + + if (UdpPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace ( + UdpPacket, + MTFTP6_DATA_HEAD_LEN, + FALSE + ); + ASSERT (Packet != NULL); + + Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA); + Packet->Data.Block = HTONS (BlockNum); + + // + // Read the block from either the buffer or PacketNeeded callback + // + Token = Instance->Token; + DataLen = Instance->BlkSize; + + if (Token->Buffer != NULL) { + Start = MultU64x32 (BlockNum - 1, Instance->BlkSize); + + if (Token->BufferSize < Start + Instance->BlkSize) { + DataLen = (UINT16) (Token->BufferSize - Start); + Instance->LastBlk = BlockNum; + Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen); + } + + } else { + // + // Get data from PacketNeeded + // + DataBuf = NULL; + Status = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf); + + if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) { + if (DataBuf != NULL) { + gBS->FreePool (DataBuf); + } + // + // The received packet has already been freed. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + + return EFI_ABORTED; + } + + if (DataLen < Instance->BlkSize) { + Instance->LastBlk = BlockNum; + Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + CopyMem (Packet->Data.Data, DataBuf, DataLen); + gBS->FreePool (DataBuf); + } + } + + // + // Reset current retry count of the instance. + // + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, UdpPacket); +} + + +/** + Function to handle received ACK packet. If the ACK number matches the + expected block number, with more data pending, send the next + block. Otherwise, tell the caller that we are done. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the upload has been completed. + Otherwise, the upload has not been completed. + + @retval EFI_SUCCESS The ACK packet successfully processed. + @retval EFI_TFTP_ERROR The block number loops back. + @retval Others Failed to transmit the next data packet. + +**/ +EFI_STATUS +Mtftp6WrqHandleAck ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + UINT16 AckNum; + INTN Expected; + UINT64 BlockCounter; + + *IsCompleted = FALSE; + AckNum = NTOHS (Packet->Ack.Block[0]); + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + ASSERT (Expected >= 0); + + // + // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput + // restart receive. + // + if (Expected != AckNum) { + return EFI_SUCCESS; + } + + // + // Remove the acked block number, if this is the last block number, + // tell the Mtftp6WrqInput to finish the transfer. This is the last + // block number if the block range are empty. + // + Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &BlockCounter); + + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Expected < 0) { + // + // The block range is empty. It may either because the last + // block has been ACKed, or the sequence number just looped back, + // that is, there is more than 0xffff blocks. + // + if (Instance->LastBlk == AckNum) { + ASSERT (Instance->LastBlk >= 1); + *IsCompleted = TRUE; + return EFI_SUCCESS; + + } else { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if block number rolls back. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "Block number rolls back, not supported, try blksize option" + ); + + return EFI_TFTP_ERROR; + } + } + + // + // Free the receive buffer before send new packet since it might need + // reconfigure udpio. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + return Mtftp6WrqSendBlock (Instance, (UINT16) Expected); +} + + +/** + Check whether the received OACK is valid. The OACK is valid + only if: + 1. It only include options requested by us. + 2. It can only include a smaller block size. + 3. It can't change the proposed time out value. + 4. Other requirements of the individal MTFTP6 options as required. + + @param[in] ReplyInfo The pointer to options information in reply packet. + @param[in] RequestInfo The pointer to requested options information. + + @retval TRUE If the option in OACK is valid. + @retval FALSE If the option is invalid. + +**/ +BOOLEAN +Mtftp6WrqOackValid ( + IN MTFTP6_EXT_OPTION_INFO *ReplyInfo, + IN MTFTP6_EXT_OPTION_INFO *RequestInfo + ) +{ + // + // It is invalid for server to return options we don't request + // + if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size to be used and + // return the timeout matches that requested. + // + if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) || + (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout)) + ) { + + return FALSE; + } + + return TRUE; +} + + +/** + Process the OACK packet for Wrq. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the upload has been completed. + Otherwise, the upload has not been completed. + + @retval EFI_SUCCESS The OACK packet successfully processed. + @retval EFI_TFTP_ERROR An TFTP communication error happened. + @retval Others Failed to process the OACK packet. + +**/ +EFI_STATUS +Mtftp6WrqHandleOack ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_MTFTP6_OPTION *Options; + UINT32 Count; + MTFTP6_EXT_OPTION_INFO ExtInfo; + EFI_MTFTP6_PACKET Dummy; + EFI_STATUS Status; + INTN Expected; + + *IsCompleted = FALSE; + Options = NULL; + + // + // Ignore the OACK if already started the upload + // + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Expected != 0) { + return EFI_SUCCESS; + } + + // + // Parse and validate the options from server + // + ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + + Status = Mtftp6ParseStart (Packet, Len, &Count, &Options); + + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Options != NULL); + + Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, Instance->Operation, &ExtInfo); + + if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) { + // + // Don't send a MTFTP error packet when out of resource, it can + // only make it worse. + // + if (Status != EFI_OUT_OF_RESOURCES) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid Oack packet received. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Malformatted OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + + // + // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck, + // which will start the transmission of the first data block. + // + Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK); + Dummy.Ack.Block[0] = 0; + + return Mtftp6WrqHandleAck ( + Instance, + &Dummy, + sizeof (EFI_MTFTP6_ACK_HEADER), + UdpPacket, + IsCompleted + ); +} + + +/** + The packet process callback for Mtftp6 upload. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PACKET *Packet; + BOOLEAN IsCompleted; + EFI_STATUS Status; + UINT32 TotalNum; + UINT32 Len; + UINT16 Opcode; + + Instance = (MTFTP6_INSTANCE *) Context; + + NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE); + + IsCompleted = FALSE; + Packet = NULL; + Status = EFI_SUCCESS; + TotalNum = 0; + + // + // Return error status if Udp6 instance failed to receive. + // + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + + // + // Client send initial request to server's listening port. Server + // will select a UDP port to communicate with the client. + // + if (UdpEpt->RemotePort != Instance->ServerDataPort) { + if (Instance->ServerDataPort != 0) { + goto ON_EXIT; + } else { + Instance->ServerDataPort = UdpEpt->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + TotalNum = UdpPacket->BlockOpNum; + + if (TotalNum > 1) { + Packet = AllocateZeroPool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + ASSERT (Packet != NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Callback to the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if (Instance->Token->CheckPacket != NULL && + (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR) + ) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp6, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP6_OPCODE_ERROR) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (UdpPacket); + UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Switch the process routines by the operation code. + // + switch (Opcode) { + case EFI_MTFTP6_OPCODE_ACK: + if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) { + goto ON_EXIT; + } + // + // Handle the Ack packet of Wrq. + // + Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted); + break; + + case EFI_MTFTP6_OPCODE_OACK: + if (Len <= MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + // + // Handle the Oack packet of Wrq. + // + Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted); + break; + + default: + // + // Drop and return eror if received error message. + // + Status = EFI_TFTP_ERROR; + break; + } + +ON_EXIT: + // + // Free the resources, then if !EFI_ERROR (Status) and not completed, + // restart the receive, otherwise end the session. + // + if (Packet != NULL && TotalNum > 1) { + FreePool (Packet); + } + + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + + if (!EFI_ERROR (Status) && !IsCompleted) { + Status = UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6WrqInput, + Instance, + 0 + ); + } + // + // Clean up the current session if failed to continue. + // + if (EFI_ERROR (Status) || IsCompleted) { + Mtftp6OperationClean (Instance, Status); + } +} + + +/** + Start the Mtftp6 instance to upload. It will first init some states, + then send the WRQ request packet, and start to receive the packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of the current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to upload. + @retval Others Failed to start to upload. + +**/ +EFI_STATUS +Mtftp6WrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ) +{ + EFI_STATUS Status; + + // + // The valid block number range are [0, 0xffff]. For example: + // the client sends an WRQ request to the server, the server + // ACK with an ACK0 to let client start transfer the first + // packet. + // + Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6SendRequest (Instance, Operation); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6WrqInput, + Instance, + 0 + ); +} + |