diff options
Diffstat (limited to 'Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c')
-rw-r--r-- | Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c | 1236 |
1 files changed, 1236 insertions, 0 deletions
diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c new file mode 100644 index 0000000000..e6b4127b78 --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c @@ -0,0 +1,1236 @@ +/** @file
+ Mtftp6 support functions implementation.
+
+ Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
+
+ 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.
+
+**/
+
+#include "Mtftp6Impl.h"
+
+
+/**
+ Allocate a MTFTP block range, then init it to the range of [Start, End].
+
+ @param[in] Start The start block number.
+ @param[in] End The last block number in the range.
+
+ @return Range The range of the allocated block buffer.
+
+**/
+MTFTP6_BLOCK_RANGE *
+Mtftp6AllocateRange (
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+
+ Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE));
+
+ if (Range == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&Range->Link);
+ Range->Start = Start;
+ Range->End = End;
+ Range->Bound = End;
+
+ return Range;
+}
+
+
+/**
+ Initialize the block range for either RRQ or WRQ. RRQ and WRQ have
+ different requirements for Start and End. For example, during startup,
+ WRQ initializes its whole valid block range to [0, 0xffff]. This
+ is bacause the server will send an ACK0 to inform the user to start the
+ upload. When the client receives an ACK0, it will remove 0 from the range,
+ get the next block number, which is 1, then upload the BLOCK1. For RRQ
+ without option negotiation, the server will directly send the BLOCK1
+ in response to the client's RRQ. When received BLOCK1, the client will
+ remove it from the block range and send an ACK. It also works if there
+ is option negotiation.
+
+ @param[in] Head The block range head to initialize.
+ @param[in] Start The Start block number.
+ @param[in] End The last block number.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range.
+ @retval EFI_SUCCESS The initial block range is created.
+
+**/
+EFI_STATUS
+Mtftp6InitBlockRange (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+
+ Range = Mtftp6AllocateRange (Start, End);
+
+ if (Range == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InsertTailList (Head, &Range->Link);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the first valid block number on the range list.
+
+ @param[in] Head The block range head.
+
+ @retval ==-1 If the block range is empty.
+ @retval >-1 The first valid block number.
+
+**/
+INTN
+Mtftp6GetNextBlockNum (
+ IN LIST_ENTRY *Head
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+
+ if (IsListEmpty (Head)) {
+ return -1;
+ }
+
+ Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link);
+ return Range->Start;
+}
+
+
+/**
+ Set the last block number of the block range list. It
+ removes all the blocks after the Last. MTFTP initialize the
+ block range to the maximum possible range, such as [0, 0xffff]
+ for WRQ. When it gets the last block number, it calls
+ this function to set the last block number.
+
+ @param[in] Head The block range list.
+ @param[in] Last The last block number.
+
+**/
+VOID
+Mtftp6SetLastBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Last
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+
+ //
+ // Iterate from the tail to head to remove the block number
+ // after the last.
+ //
+ while (!IsListEmpty (Head)) {
+ Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link);
+
+ if (Range->Start > Last) {
+ RemoveEntryList (&Range->Link);
+ FreePool (Range);
+ continue;
+ }
+
+ if (Range->End > Last) {
+ Range->End = Last;
+ }
+ return ;
+ }
+}
+
+
+/**
+ Remove the block number from the block range list.
+
+ @param[in] Head The block range list to remove from.
+ @param[in] Num The block number to remove.
+ @param[in] Completed Whether Num is the last block number
+ @param[out] TotalBlock The continuous block number in all
+
+ @retval EFI_NOT_FOUND The block number isn't in the block range list.
+ @retval EFI_SUCCESS The block number has been removed from the list.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+
+**/
+EFI_STATUS
+Mtftp6RemoveBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Num,
+ IN BOOLEAN Completed,
+ OUT UINT64 *TotalBlock
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+ MTFTP6_BLOCK_RANGE *NewRange;
+ LIST_ENTRY *Entry;
+
+ NET_LIST_FOR_EACH (Entry, Head) {
+
+ //
+ // Each block represents a hole [Start, End] in the file,
+ // skip to the first range with End >= Num
+ //
+ Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);
+
+ if (Range->End < Num) {
+ continue;
+ }
+
+ //
+ // There are three different cases for Start
+ // 1. (Start > Num) && (End >= Num):
+ // because all the holes before this one has the condition of
+ // End < Num, so this block number has been removed.
+ //
+ // 2. (Start == Num) && (End >= Num):
+ // Need to increase the Start by one, and if End == Num, this
+ // hole has been removed completely, remove it.
+ //
+ // 3. (Start < Num) && (End >= Num):
+ // if End == Num, only need to decrease the End by one because
+ // we have (Start < Num) && (Num == End), so (Start <= End - 1).
+ // if (End > Num), the hold is splited into two holes, with
+ // [Start, Num - 1] and [Num + 1, End].
+ //
+ if (Range->Start > Num) {
+ return EFI_NOT_FOUND;
+
+ } else if (Range->Start == Num) {
+ Range->Start++;
+
+ //
+ // Note that: RFC 1350 does not mention block counter roll-over,
+ // but several TFTP hosts implement the roll-over be able to accept
+ // transfers of unlimited size. There is no consensus, however, whether
+ // the counter should wrap around to zero or to one. Many implementations
+ // wrap to zero, because this is the simplest to implement. Here we choose
+ // this solution.
+ //
+ *TotalBlock = Num;
+
+ if (Range->Round > 0) {
+ *TotalBlock += Range->Bound + MultU64x32 (Range->Round - 1, (UINT32)(Range->Bound + 1)) + 1;
+ }
+
+ if (Range->Start > Range->Bound) {
+ Range->Start = 0;
+ Range->Round ++;
+ }
+
+ if ((Range->Start > Range->End) || Completed) {
+ RemoveEntryList (&Range->Link);
+ FreePool (Range);
+ }
+
+ return EFI_SUCCESS;
+
+ } else {
+ if (Range->End == Num) {
+ Range->End--;
+ } else {
+ NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);
+
+ if (NewRange == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Range->End = Num - 1;
+ NetListInsertAfter (&Range->Link, &NewRange->Link);
+ }
+
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Configure the opened Udp6 instance until the corresponding Ip6 instance
+ has been configured.
+
+ @param[in] UdpIo The pointer to the Udp6 Io.
+ @param[in] UdpCfgData The pointer to the Udp6 configure data.
+
+ @retval EFI_SUCCESS Configure the Udp6 instance successfully.
+ @retval EFI_NO_MAPPING The corresponding Ip6 instance has not
+ been configured yet.
+
+**/
+EFI_STATUS
+Mtftp6GetMapping (
+ IN UDP_IO *UdpIo,
+ IN EFI_UDP6_CONFIG_DATA *UdpCfgData
+ )
+{
+ EFI_IP6_MODE_DATA Ip6Mode;
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ Event = NULL;
+ Udp6 = UdpIo->Protocol.Udp6;
+
+ //
+ // Create a timer to check whether the Ip6 instance configured or not.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = gBS->SetTimer (
+ Event,
+ TimerRelative,
+ MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Check the Ip6 mode data till timeout.
+ //
+ while (EFI_ERROR (gBS->CheckEvent (Event))) {
+
+ Udp6->Poll (Udp6);
+
+ Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL);
+
+ if (!EFI_ERROR (Status)) {
+ if (Ip6Mode.AddressList != NULL) {
+ FreePool (Ip6Mode.AddressList);
+ }
+
+ if (Ip6Mode.GroupTable != NULL) {
+ FreePool (Ip6Mode.GroupTable);
+ }
+
+ if (Ip6Mode.RouteTable != NULL) {
+ FreePool (Ip6Mode.RouteTable);
+ }
+
+ if (Ip6Mode.NeighborCache != NULL) {
+ FreePool (Ip6Mode.NeighborCache);
+ }
+
+ if (Ip6Mode.PrefixTable != NULL) {
+ FreePool (Ip6Mode.PrefixTable);
+ }
+
+ if (Ip6Mode.IcmpTypeList != NULL) {
+ FreePool (Ip6Mode.IcmpTypeList);
+ }
+
+ if (Ip6Mode.IsConfigured) {
+ //
+ // Continue to configure the Udp6 instance.
+ //
+ Status = Udp6->Configure (Udp6, UdpCfgData);
+ } else {
+ Status = EFI_NO_MAPPING;
+ }
+ }
+ }
+
+ON_EXIT:
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ return Status;
+}
+
+
+/**
+ The dummy configure routine for create a new Udp6 Io.
+
+ @param[in] UdpIo The pointer to the Udp6 Io.
+ @param[in] Context The pointer to the context.
+
+ @retval EFI_SUCCESS This value is always returned.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ConfigDummyUdpIo (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The configure routine for Mtftp6 instance to transmit/receive.
+
+ @param[in] UdpIo The pointer to the Udp6 Io.
+ @param[in] ServerIp The pointer to the server address.
+ @param[in] ServerPort The pointer to the server port.
+ @param[in] LocalIp The pointer to the local address.
+ @param[in] LocalPort The pointer to the local port.
+
+ @retval EFI_SUCCESS Configured the Udp6 Io for Mtftp6 successfully.
+ @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been
+ configured yet.
+
+**/
+EFI_STATUS
+Mtftp6ConfigUdpIo (
+ IN UDP_IO *UdpIo,
+ IN EFI_IPv6_ADDRESS *ServerIp,
+ IN UINT16 ServerPort,
+ IN EFI_IPv6_ADDRESS *LocalIp,
+ IN UINT16 LocalPort
+ )
+{
+ EFI_STATUS Status;
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_UDP6_CONFIG_DATA *Udp6Cfg;
+
+ Udp6 = UdpIo->Protocol.Udp6;
+ Udp6Cfg = &(UdpIo->Config.Udp6);
+
+ ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));
+
+ //
+ // Set the Udp6 Io configure data.
+ //
+ Udp6Cfg->AcceptPromiscuous = FALSE;
+ Udp6Cfg->AcceptAnyPort = FALSE;
+ Udp6Cfg->AllowDuplicatePort = FALSE;
+ Udp6Cfg->TrafficClass = 0;
+ Udp6Cfg->HopLimit = 128;
+ Udp6Cfg->ReceiveTimeout = 0;
+ Udp6Cfg->TransmitTimeout = 0;
+ Udp6Cfg->StationPort = LocalPort;
+ Udp6Cfg->RemotePort = ServerPort;
+
+ CopyMem (
+ &Udp6Cfg->StationAddress,
+ LocalIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ CopyMem (
+ &Udp6Cfg->RemoteAddress,
+ ServerIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ //
+ // Configure the Udp6 instance with current configure data.
+ //
+ Status = Udp6->Configure (Udp6, Udp6Cfg);
+
+ if (Status == EFI_NO_MAPPING) {
+
+ return Mtftp6GetMapping (UdpIo, Udp6Cfg);
+ }
+
+ return Status;
+}
+
+
+/**
+ Build and transmit the request packet for the Mtftp6 instance.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Operation The operation code of this packet.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request.
+ @retval EFI_SUCCESS The request is built and sent.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp6SendRequest (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_MTFTP6_PACKET *Packet;
+ EFI_MTFTP6_OPTION *Options;
+ EFI_MTFTP6_TOKEN *Token;
+ RETURN_STATUS Status;
+ NET_BUF *Nbuf;
+ UINT8 *Mode;
+ UINT8 *Cur;
+ UINTN Index;
+ UINT32 BufferLength;
+ UINTN FileNameLength;
+ UINTN ModeLength;
+ UINTN OptionStrLength;
+ UINTN ValueStrLength;
+
+ Token = Instance->Token;
+ Options = Token->OptionList;
+ Mode = Token->ModeStr;
+
+ if (Mode == NULL) {
+ Mode = (UINT8 *) "octet";
+ }
+
+ //
+ // The header format of RRQ/WRQ packet is:
+ //
+ // 2 bytes string 1 byte string 1 byte
+ // ------------------------------------------------
+ // | Opcode | Filename | 0 | Mode | 0 |
+ // ------------------------------------------------
+ //
+ // The common option format is:
+ //
+ // string 1 byte string 1 byte
+ // ---------------------------------------
+ // | OptionStr | 0 | ValueStr | 0 |
+ // ---------------------------------------
+ //
+
+ //
+ // Compute the size of new Mtftp6 packet.
+ //
+ FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);
+ ModeLength = AsciiStrLen ((CHAR8 *) Mode);
+ BufferLength = (UINT32) FileNameLength + (UINT32) ModeLength + 4;
+
+ for (Index = 0; Index < Token->OptionCount; Index++) {
+ OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
+ ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
+ BufferLength += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;
+ }
+
+ //
+ // Allocate a packet then copy the data.
+ //
+ if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy the opcode, filename and mode into packet.
+ //
+ Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);
+ ASSERT (Packet != NULL);
+
+ Packet->OpCode = HTONS (Operation);
+ BufferLength -= sizeof (Packet->OpCode);
+
+ Cur = Packet->Rrq.Filename;
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (FileNameLength + 1);
+ Cur += FileNameLength + 1;
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (ModeLength + 1);
+ Cur += ModeLength + 1;
+
+ //
+ // Copy all the extension options into the packet.
+ //
+ for (Index = 0; Index < Token->OptionCount; ++Index) {
+ OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
+ ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
+
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (OptionStrLength + 1);
+ Cur += OptionStrLength + 1;
+
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (ValueStrLength + 1);
+ Cur += ValueStrLength + 1;
+
+ }
+
+ //
+ // Save the packet buf for retransmit
+ //
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ }
+
+ Instance->LastPacket = Nbuf;
+ Instance->CurRetry = 0;
+
+ return Mtftp6TransmitPacket (Instance, Nbuf);
+}
+
+
+/**
+ Build and send an error packet.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] ErrCode The error code in the packet.
+ @param[in] ErrInfo The error message in the packet.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet.
+ @retval EFI_SUCCESS The error packet is transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp6SendError (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 ErrCode,
+ IN UINT8* ErrInfo
+ )
+{
+ NET_BUF *Nbuf;
+ EFI_MTFTP6_PACKET *TftpError;
+ UINT32 Len;
+
+ //
+ // Allocate a packet then copy the data.
+ //
+ Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER));
+ Nbuf = NetbufAlloc (Len);
+
+ if (Nbuf == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);
+
+ if (TftpError == NULL) {
+ NetbufFree (Nbuf);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TftpError->OpCode = HTONS (EFI_MTFTP6_OPCODE_ERROR);
+ TftpError->Error.ErrorCode = HTONS (ErrCode);
+
+ AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, AsciiStrLen ((CHAR8 *) ErrInfo) + 1 , (CHAR8 *) ErrInfo);
+
+ //
+ // Save the packet buf for retransmit
+ //
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ }
+
+ Instance->LastPacket = Nbuf;
+ Instance->CurRetry = 0;
+
+ return Mtftp6TransmitPacket (Instance, Nbuf);
+}
+
+
+/**
+ The callback function called when the packet is transmitted.
+
+ @param[in] Packet The pointer to the packet.
+ @param[in] UdpEpt The pointer to the Udp6 access point.
+ @param[in] IoStatus The result of the transmission.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6OnPacketSent (
+ IN NET_BUF *Packet,
+ IN UDP_END_POINT *UdpEpt,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ NetbufFree (Packet);
+ *(BOOLEAN *) Context = TRUE;
+}
+
+
+/**
+ Send the packet for the Mtftp6 instance.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the packet to be sent.
+
+ @retval EFI_SUCCESS The packet was sent out
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp6TransmitPacket (
+ IN MTFTP6_INSTANCE *Instance,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_UDP6_CONFIG_DATA Udp6CfgData;
+ EFI_STATUS Status;
+ UINT16 *Temp;
+ UINT16 Value;
+ UINT16 OpCode;
+
+ ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA));
+ Udp6 = Instance->UdpIo->Protocol.Udp6;
+
+ //
+ // Set the live time of the packet.
+ //
+ Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2);
+
+ Temp = (UINT16 *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Temp != NULL);
+
+ Value = *Temp;
+ OpCode = NTOHS (Value);
+
+ if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) {
+ //
+ // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as
+ // (serverip, 69, localip, localport) to send.
+ // Usually local address and local port are both default as zero.
+ //
+ Status = Udp6->Configure (Udp6, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6ConfigUdpIo (
+ Instance->UdpIo,
+ &Instance->ServerIp,
+ Instance->ServerCmdPort,
+ &Instance->Config->StationIp,
+ Instance->Config->LocalPort
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the current local address and port by get Udp6 mode data.
+ //
+ Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ NET_GET_REF (Packet);
+
+ Instance->IsTransmitted = FALSE;
+
+ Status = UdpIoSendDatagram (
+ Instance->UdpIo,
+ Packet,
+ NULL,
+ NULL,
+ Mtftp6OnPacketSent,
+ &Instance->IsTransmitted
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Packet);
+ return Status;
+ }
+
+ //
+ // Poll till the packet sent out from the ip6 queue.
+ //
+ gBS->RestoreTPL (Instance->OldTpl);
+
+ while (!Instance->IsTransmitted) {
+ Udp6->Poll (Udp6);
+ }
+
+ Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // For the subsequent exchange of such requests, reconfigure the Udp6Io as
+ // (serverip, 0, localip, localport) to receive.
+ // Currently local address and local port are specified by Udp6 mode data.
+ //
+ Status = Udp6->Configure (Udp6, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6ConfigUdpIo (
+ Instance->UdpIo,
+ &Instance->ServerIp,
+ Instance->ServerDataPort,
+ &Udp6CfgData.StationAddress,
+ Udp6CfgData.StationPort
+ );
+ } else {
+ //
+ // For the data exchange, configure the Udp6Io as (serverip, dataport,
+ // localip, localport) to send/receive.
+ // Currently local address and local port are specified by Udp6 mode data.
+ //
+ Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Udp6CfgData.RemotePort != Instance->ServerDataPort) {
+
+ Status = Udp6->Configure (Udp6, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6ConfigUdpIo (
+ Instance->UdpIo,
+ &Instance->ServerIp,
+ Instance->ServerDataPort,
+ &Udp6CfgData.StationAddress,
+ Udp6CfgData.StationPort
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ NET_GET_REF (Packet);
+
+ Instance->IsTransmitted = FALSE;
+
+ Status = UdpIoSendDatagram (
+ Instance->UdpIo,
+ Packet,
+ NULL,
+ NULL,
+ Mtftp6OnPacketSent,
+ &Instance->IsTransmitted
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Packet);
+ }
+
+ //
+ // Poll till the packet sent out from the ip6 queue.
+ //
+ gBS->RestoreTPL (Instance->OldTpl);
+
+ while (!Instance->IsTransmitted) {
+ Udp6->Poll (Udp6);
+ }
+
+ Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ }
+
+ return Status;
+}
+
+
+/**
+ Check packet for GetInfo callback routine.
+
+ GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect
+ the first packet from server, then abort the session.
+
+ @param[in] This The pointer to the Mtftp6 protocol.
+ @param[in] Token The pointer to the Mtftp6 token.
+ @param[in] PacketLen The length of the packet.
+ @param[in] Packet The pointer to the received packet.
+
+ @retval EFI_ABORTED Abort the Mtftp6 operation.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6CheckPacket (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token,
+ IN UINT16 PacketLen,
+ IN EFI_MTFTP6_PACKET *Packet
+ )
+{
+ MTFTP6_GETINFO_CONTEXT *Context;
+ UINT16 OpCode;
+
+ Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context;
+ OpCode = NTOHS (Packet->OpCode);
+
+ //
+ // Set the GetInfo's return status according to the OpCode.
+ //
+ switch (OpCode) {
+ case EFI_MTFTP6_OPCODE_ERROR:
+ Context->Status = EFI_TFTP_ERROR;
+ break;
+
+ case EFI_MTFTP6_OPCODE_OACK:
+ Context->Status = EFI_SUCCESS;
+ break;
+
+ default:
+ Context->Status = EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // Allocate buffer then copy the packet over. Use gBS->AllocatePool
+ // in case NetAllocatePool will implements something tricky.
+ //
+ *(Context->Packet) = AllocateZeroPool (PacketLen);
+
+ if (*(Context->Packet) == NULL) {
+ Context->Status = EFI_OUT_OF_RESOURCES;
+ return EFI_ABORTED;
+ }
+
+ *(Context->PacketLen) = PacketLen;
+ CopyMem (*(Context->Packet), Packet, PacketLen);
+
+ return EFI_ABORTED;
+}
+
+
+/**
+ Clean up the current Mtftp6 operation.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Result The result to be returned to the user.
+
+**/
+VOID
+Mtftp6OperationClean (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_STATUS Result
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ MTFTP6_BLOCK_RANGE *Block;
+
+ //
+ // Clean up the current token and event.
+ //
+ if (Instance->Token != NULL) {
+ Instance->Token->Status = Result;
+ if (Instance->Token->Event != NULL) {
+ gBS->SignalEvent (Instance->Token->Event);
+ }
+ Instance->Token = NULL;
+ }
+
+ //
+ // Clean up the corresponding Udp6Io.
+ //
+ if (Instance->UdpIo != NULL) {
+ UdpIoCleanIo (Instance->UdpIo);
+ }
+
+ if (Instance->McastUdpIo != NULL) {
+ gBS->CloseProtocol (
+ Instance->McastUdpIo->UdpHandle,
+ &gEfiUdp6ProtocolGuid,
+ Instance->McastUdpIo->Image,
+ Instance->Handle
+ );
+ UdpIoFreeIo (Instance->McastUdpIo);
+ Instance->McastUdpIo = NULL;
+ }
+
+ //
+ // Clean up the stored last packet.
+ //
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ Instance->LastPacket = NULL;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {
+ Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);
+ RemoveEntryList (Entry);
+ FreePool (Block);
+ }
+
+ //
+ // Reinitialize the corresponding fields of the Mtftp6 operation.
+ //
+ ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));
+ ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS));
+ ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));
+
+ Instance->ServerCmdPort = 0;
+ Instance->ServerDataPort = 0;
+ Instance->McastPort = 0;
+ Instance->BlkSize = 0;
+ Instance->LastBlk = 0;
+ Instance->PacketToLive = 0;
+ Instance->MaxRetry = 0;
+ Instance->CurRetry = 0;
+ Instance->Timeout = 0;
+ Instance->IsMaster = TRUE;
+}
+
+
+/**
+ Start the Mtftp6 instance to perform the operation, such as read file,
+ write file, and read directory.
+
+ @param[in] This The MTFTP session.
+ @param[in] Token The token than encapsues the user's request.
+ @param[in] OpCode The operation to perform.
+
+ @retval EFI_INVALID_PARAMETER Some of the parameters are invalid.
+ @retval EFI_NOT_STARTED The MTFTP session hasn't been configured.
+ @retval EFI_ALREADY_STARTED There is pending operation for the session.
+ @retval EFI_SUCCESS The operation is successfully started.
+
+**/
+EFI_STATUS
+Mtftp6OperationStart (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token,
+ IN UINT16 OpCode
+ )
+{
+ MTFTP6_INSTANCE *Instance;
+ EFI_STATUS Status;
+
+ if (This == NULL ||
+ Token == NULL ||
+ Token->Filename == NULL ||
+ (Token->OptionCount != 0 && Token->OptionList == NULL) ||
+ (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp))
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // At least define one method to collect the data for download.
+ //
+ if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) &&
+ Token->Buffer == NULL &&
+ Token->CheckPacket == NULL
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // At least define one method to provide the data for upload.
+ //
+ if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP6_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Config == NULL) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Instance->Token != NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ Status = EFI_SUCCESS;
+ Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Parse the extension options in the request packet.
+ //
+ if (Token->OptionCount != 0) {
+
+ Status = Mtftp6ParseExtensionOption (
+ Token->OptionList,
+ Token->OptionCount,
+ TRUE,
+ &Instance->ExtInfo
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Initialize runtime data from config data or override data.
+ //
+ Instance->Token = Token;
+ Instance->ServerCmdPort = Instance->Config->InitialServerPort;
+ Instance->ServerDataPort = 0;
+ Instance->MaxRetry = Instance->Config->TryCount;
+ Instance->Timeout = Instance->Config->TimeoutValue;
+ Instance->IsMaster = TRUE;
+
+ CopyMem (
+ &Instance->ServerIp,
+ &Instance->Config->ServerIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ if (Token->OverrideData != NULL) {
+ Instance->ServerCmdPort = Token->OverrideData->ServerPort;
+ Instance->MaxRetry = Token->OverrideData->TryCount;
+ Instance->Timeout = Token->OverrideData->TimeoutValue;
+
+ CopyMem (
+ &Instance->ServerIp,
+ &Token->OverrideData->ServerIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ }
+
+ //
+ // Set default value for undefined parameters.
+ //
+ if (Instance->ServerCmdPort == 0) {
+ Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT;
+ }
+ if (Instance->BlkSize == 0) {
+ Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE;
+ }
+ if (Instance->MaxRetry == 0) {
+ Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY;
+ }
+ if (Instance->Timeout == 0) {
+ Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT;
+ }
+
+ Token->Status = EFI_NOT_READY;
+
+ //
+ // Switch the routines by the operation code.
+ //
+ switch (OpCode) {
+ case EFI_MTFTP6_OPCODE_RRQ:
+ Status = Mtftp6RrqStart (Instance, OpCode);
+ break;
+
+ case EFI_MTFTP6_OPCODE_DIR:
+ Status = Mtftp6RrqStart (Instance, OpCode);
+ break;
+
+ case EFI_MTFTP6_OPCODE_WRQ:
+ Status = Mtftp6WrqStart (Instance, OpCode);
+ break;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Return immediately for asynchronous or poll the instance for synchronous.
+ //
+ gBS->RestoreTPL (Instance->OldTpl);
+
+ if (Token->Event == NULL) {
+ while (Token->Status == EFI_NOT_READY) {
+ This->Poll (This);
+ }
+ return Token->Status;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ Mtftp6OperationClean (Instance, Status);
+ gBS->RestoreTPL (Instance->OldTpl);
+
+ return Status;
+}
+
+
+/**
+ The timer ticking routine for the Mtftp6 instance.
+
+ @param[in] Event The pointer to the ticking event.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MTFTP6_SERVICE *Service;
+ MTFTP6_INSTANCE *Instance;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ EFI_MTFTP6_TOKEN *Token;
+ EFI_STATUS Status;
+
+ Service = (MTFTP6_SERVICE *) Context;
+
+ //
+ // Iterate through all the children of the Mtftp service instance. Time
+ // out the packet. If maximum retries reached, clean the session up.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) {
+
+ Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link);
+
+ if (Instance->Token == NULL) {
+ continue;
+ }
+
+ if (Instance->PacketToLive > 0) {
+ Instance->PacketToLive--;
+ continue;
+ }
+
+ Instance->CurRetry++;
+ Token = Instance->Token;
+
+ if (Token->TimeoutCallback != NULL) {
+ //
+ // Call the timeout callback routine if has.
+ //
+ Status = Token->TimeoutCallback (&Instance->Mtftp6, Token);
+
+ if (EFI_ERROR (Status)) {
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer in time out"
+ );
+ Mtftp6OperationClean (Instance, EFI_ABORTED);
+ continue;
+ }
+ }
+
+ //
+ // Retransmit the packet if haven't reach the maxmium retry count,
+ // otherwise exit the transfer.
+ //
+ if (Instance->CurRetry < Instance->MaxRetry) {
+ Mtftp6TransmitPacket (Instance, Instance->LastPacket);
+ } else {
+ Mtftp6OperationClean (Instance, EFI_TIMEOUT);
+ continue;
+ }
+ }
+}
|