From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- .../EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c | 529 +++++++++++++++++++++ 1 file changed, 529 insertions(+) create mode 100644 src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c') diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c new file mode 100644 index 00000000..edd8c5e0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c @@ -0,0 +1,529 @@ +/** @file + Routines to process Wrq (upload). + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Mtftp4Impl.h" + + + +/** + Build then send a MTFTP data packet for the MTFTP upload session. + + @param Instance The MTFTP upload session. + @param BlockNum The block number to send. + + @retval EFI_OUT_OF_RESOURCES Failed to build the packet. + @retval EFI_ABORTED The consumer of this child directs to abort the + transmission by return an error through PacketNeeded. + @retval EFI_SUCCESS The data is sent. + +**/ +EFI_STATUS +Mtftp4WrqSendBlock ( + IN OUT MTFTP4_PROTOCOL *Instance, + IN UINT16 BlockNum + ) +{ + EFI_MTFTP4_PACKET *Packet; + EFI_MTFTP4_TOKEN *Token; + NET_BUF *UdpPacket; + EFI_STATUS Status; + UINT16 DataLen; + UINT8 *DataBuf; + UINT64 Start; + + // + // Allocate a buffer to hold the user data + // + UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN); + + if (UdpPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE); + ASSERT (Packet != NULL); + + Packet->Data.OpCode = HTONS (EFI_MTFTP4_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->LastBlock = BlockNum; + Mtftp4SetLastBlockNum (&Instance->Blocks, 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->Mtftp4, + Token, + &DataLen, + (VOID **) &DataBuf + ); + + if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) { + if (DataBuf != NULL) { + FreePool (DataBuf); + } + + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + + return EFI_ABORTED; + } + + if (DataLen < Instance->BlkSize) { + Instance->LastBlock = BlockNum; + Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + CopyMem (Packet->Data.Data, DataBuf, DataLen); + FreePool (DataBuf); + } + } + + return Mtftp4SendPacket (Instance, UdpPacket); +} + + +/** + Function to handle received ACK packet. + + If the ACK number matches the expected block number, and there are more + data pending, send the next block. Otherwise tell the caller that we are done. + + @param Instance The MTFTP upload session + @param Packet The MTFTP packet received + @param Len The packet length + @param Completed Return whether the upload has finished. + + @retval EFI_SUCCESS The ACK is successfully processed. + @retval EFI_TFTP_ERROR The block number loops back. + @retval Others Failed to transmit the next data packet. + +**/ +EFI_STATUS +Mtftp4WrqHandleAck ( + IN MTFTP4_PROTOCOL *Instance, + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 Len, + OUT BOOLEAN *Completed + ) +{ + UINT16 AckNum; + INTN Expected; + UINT64 BlockCounter; + + *Completed = FALSE; + AckNum = NTOHS (Packet->Ack.Block[0]); + Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); + + ASSERT (Expected >= 0); + + // + // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput + // restart receive. + // + if (Expected != AckNum) { + return EFI_SUCCESS; + } + + // + // Remove the acked block number, if this is the last block number, + // tell the Mtftp4WrqInput to finish the transfer. This is the last + // block number if the block range are empty. + // + Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum, *Completed, &BlockCounter); + + Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); + + 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->LastBlock == AckNum) { + ASSERT (Instance->LastBlock >= 1); + *Completed = TRUE; + return EFI_SUCCESS; + + } else { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "Block number rolls back, not supported, try blksize option" + ); + + return EFI_TFTP_ERROR; + } + } + + return Mtftp4WrqSendBlock (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 MTFTP options as required. + + @param Reply The options included in the OACK + @param Request The options we requested + + @retval TRUE The options included in OACK is valid. + @retval FALSE The options included in OACK is invalid. + +**/ +BOOLEAN +Mtftp4WrqOackValid ( + IN MTFTP4_OPTION *Reply, + IN MTFTP4_OPTION *Request + ) +{ + // + // It is invalid for server to return options we don't request + // + if ((Reply->Exist & ~Request->Exist) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size to be used and + // return the timeout matches that requested. + // + if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) || + (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) { + return FALSE; + } + + return TRUE; +} + + +/** + Function to handle the MTFTP OACK packet. + + It parses the packet's options, and update the internal states of the session. + + @param Instance The MTFTP session + @param Packet The received OACK packet + @param Len The length of the packet + @param Completed Whether the transmission has completed. NOT used by + this function. + + @retval EFI_SUCCESS The OACK process is OK + @retval EFI_TFTP_ERROR Some error occurred, and the session reset. + +**/ +EFI_STATUS +Mtftp4WrqHandleOack ( + IN OUT MTFTP4_PROTOCOL *Instance, + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 Len, + OUT BOOLEAN *Completed + ) +{ + MTFTP4_OPTION Reply; + EFI_MTFTP4_PACKET Bogus; + EFI_STATUS Status; + INTN Expected; + + *Completed = FALSE; + + // + // Ignore the OACK if already started the upload + // + Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); + + if (Expected != 0) { + return EFI_SUCCESS; + } + + // + // Parse and validate the options from server + // + ZeroMem (&Reply, sizeof (MTFTP4_OPTION)); + Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply); + + if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) { + // + // Don't send a MTFTP error packet when out of resource, it can + // only make it worse. + // + if (Status != EFI_OUT_OF_RESOURCES) { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Malformatted OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if (Reply.BlkSize != 0) { + Instance->BlkSize = Reply.BlkSize; + } + + if (Reply.Timeout != 0) { + Instance->Timeout = Reply.Timeout; + } + + // + // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck, + // which will start the transmission of the first data block. + // + Bogus.Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK); + Bogus.Ack.Block[0] = 0; + + Status = Mtftp4WrqHandleAck ( + Instance, + &Bogus, + sizeof (EFI_MTFTP4_ACK_HEADER), + Completed + ); + + return Status; +} + + +/** + The input process routine for MTFTP upload. + + @param UdpPacket The received MTFTP packet. + @param EndPoint The local/remote access point + @param IoStatus The result of the packet receiving + @param Context Opaque parameter for the callback, which is the + MTFTP session. +**/ +VOID +EFIAPI +Mtftp4WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP4_PROTOCOL *Instance; + EFI_MTFTP4_PACKET *Packet; + BOOLEAN Completed; + EFI_STATUS Status; + UINT32 Len; + UINT16 Opcode; + + Instance = (MTFTP4_PROTOCOL *) Context; + NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE); + + Completed = FALSE; + Packet = NULL; + Status = EFI_SUCCESS; + + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + if (UdpPacket->TotalSize < MTFTP4_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 (EndPoint->RemotePort != Instance->ConnectedPort) { + if (Instance->ConnectedPort != 0) { + goto ON_EXIT; + } else { + Instance->ConnectedPort = EndPoint->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + + if (UdpPacket->BlockOpNum > 1) { + Packet = AllocatePool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + ASSERT (Packet != NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Call the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if ((Instance->Token->CheckPacket != NULL) && + ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp4, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP4_OPCODE_ERROR) { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + switch (Opcode) { + case EFI_MTFTP4_OPCODE_ACK: + if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) { + goto ON_EXIT; + } + + Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed); + break; + + case EFI_MTFTP4_OPCODE_OACK: + if (Len <= MTFTP4_OPCODE_LEN) { + goto ON_EXIT; + } + + Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed); + break; + + case EFI_MTFTP4_OPCODE_ERROR: + Status = EFI_TFTP_ERROR; + break; + + default: + break; + } + +ON_EXIT: + // + // Free the resources, then if !EFI_ERROR (Status) and not completed, + // restart the receive, otherwise end the session. + // + if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) { + FreePool (Packet); + } + + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + + if (!EFI_ERROR (Status) && !Completed) { + Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0); + } + + // + // Status may have been updated by UdpIoRecvDatagram + // + if (EFI_ERROR (Status) || Completed) { + Mtftp4CleanOperation (Instance, Status); + } +} + + + +/** + Start the MTFTP session for upload. + + It will first init some states, then send the WRQ request packet, + and start receiving the packet. + + @param Instance The MTFTP session + @param Operation Redundant parameter, which is always + EFI_MTFTP4_OPCODE_WRQ here. + + @retval EFI_SUCCESS The upload process has been started. + @retval Others Failed to start the upload. + +**/ +EFI_STATUS +Mtftp4WrqStart ( + IN MTFTP4_PROTOCOL *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 = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp4SendRequest (Instance); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0); +} + -- cgit v1.2.3