mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-11-04 06:52:16 +00:00 
			
		
		
		
	- Fix EFI_IPv4_ADDRESS usages to use a macro to copy the structure instead of direct assignment, to avoid runtime alignment errors. - Fix a EFI_INPUT_KEY usage in TerminalDxe to use CopyMem() to copy the structure instead of direct assignment, to avoid runtime alignment error. - Delete excess local variables that are initialized but otherwise unused. - CompilerIntrinsicsLib library now imported for AARCH64, as well as ARM. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Randy Pawell <randy_pawell@hp.com> Reviewed-by: Olivier Martin <Olivier.Martin@arm.com> Reviewed-by: Feng Tian <feng.tian@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16471 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			796 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			796 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Routines to process Rrq (download).
 | 
						|
  
 | 
						|
(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
 | 
						|
Copyright (c) 2006 - 2014, 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<BR>
 | 
						|
 | 
						|
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 "Mtftp4Impl.h"
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  The packet process callback for MTFTP download.
 | 
						|
 | 
						|
  @param  UdpPacket             The packet received
 | 
						|
  @param  EndPoint              The local/remote access point of the packet
 | 
						|
  @param  IoStatus              The status of the receiving
 | 
						|
  @param  Context               Opaque parameter, which is the MTFTP session
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
Mtftp4RrqInput (
 | 
						|
  IN NET_BUF                *UdpPacket,
 | 
						|
  IN UDP_END_POINT          *EndPoint,
 | 
						|
  IN EFI_STATUS             IoStatus,
 | 
						|
  IN VOID                   *Context
 | 
						|
  );
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Start the MTFTP session to download. 
 | 
						|
  
 | 
						|
  It will first initialize some of the internal states then build and send a RRQ 
 | 
						|
  reqeuest packet, at last, it will start receive for the downloading.
 | 
						|
 | 
						|
  @param  Instance              The Mtftp session
 | 
						|
  @param  Operation             The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
 | 
						|
                                or EFI_MTFTP4_OPCODE_DIR.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The mtftp download session is started.
 | 
						|
  @retval Others                Failed to start downloading.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Mtftp4RrqStart (
 | 
						|
  IN MTFTP4_PROTOCOL        *Instance,
 | 
						|
  IN UINT16                 Operation
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                Status;
 | 
						|
 | 
						|
  //
 | 
						|
  // The valid block number range are [1, 0xffff]. For example:
 | 
						|
  // the client sends an RRQ request to the server, the server
 | 
						|
  // transfers the DATA1 block. If option negoitation is ongoing,
 | 
						|
  // the server will send back an OACK, then client will send ACK0.
 | 
						|
  //
 | 
						|
  Status = Mtftp4InitBlockRange (&Instance->Blocks, 1, 0xffff);
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = Mtftp4SendRequest (Instance);
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Build and send a ACK packet for the download session.
 | 
						|
 | 
						|
  @param  Instance              The Mtftp session
 | 
						|
  @param  BlkNo                 The BlkNo to ack.
 | 
						|
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the packet
 | 
						|
  @retval EFI_SUCCESS           The ACK has been sent
 | 
						|
  @retval Others                Failed to send the ACK.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Mtftp4RrqSendAck (
 | 
						|
  IN MTFTP4_PROTOCOL        *Instance,
 | 
						|
  IN UINT16                 BlkNo
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_MTFTP4_PACKET         *Ack;
 | 
						|
  NET_BUF                   *Packet;
 | 
						|
 | 
						|
  Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));
 | 
						|
  if (Packet == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Ack = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (
 | 
						|
                                Packet,
 | 
						|
                                sizeof (EFI_MTFTP4_ACK_HEADER),
 | 
						|
                                FALSE
 | 
						|
                                );
 | 
						|
  ASSERT (Ack != NULL);
 | 
						|
 | 
						|
  Ack->Ack.OpCode   = HTONS (EFI_MTFTP4_OPCODE_ACK);
 | 
						|
  Ack->Ack.Block[0] = HTONS (BlkNo);
 | 
						|
 | 
						|
  return Mtftp4SendPacket (Instance, Packet);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Deliver the received data block to the user, which can be saved
 | 
						|
  in the user provide buffer or through the CheckPacket callback.
 | 
						|
 | 
						|
  @param  Instance              The Mtftp session
 | 
						|
  @param  Packet                The received data packet
 | 
						|
  @param  Len                   The packet length
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The data is saved successfully
 | 
						|
  @retval EFI_ABORTED           The user tells to abort by return an error  through
 | 
						|
                                CheckPacket
 | 
						|
  @retval EFI_BUFFER_TOO_SMALL  The user's buffer is too small and buffer length is
 | 
						|
                                 updated to the actual buffer size needed.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Mtftp4RrqSaveBlock (
 | 
						|
  IN OUT MTFTP4_PROTOCOL        *Instance,
 | 
						|
  IN     EFI_MTFTP4_PACKET      *Packet,
 | 
						|
  IN     UINT32                 Len
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_MTFTP4_TOKEN          *Token;
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  UINT16                    Block;
 | 
						|
  UINT64                    Start;
 | 
						|
  UINT32                    DataLen;
 | 
						|
  UINT64                    TotalBlock;
 | 
						|
  BOOLEAN                   Completed;
 | 
						|
 | 
						|
  Completed = FALSE;
 | 
						|
  Token     = Instance->Token;
 | 
						|
  Block     = NTOHS (Packet->Data.Block);
 | 
						|
  DataLen   = Len - MTFTP4_DATA_HEAD_LEN;
 | 
						|
 | 
						|
  //
 | 
						|
  // This is the last block, save the block no
 | 
						|
  //
 | 
						|
  if (DataLen < Instance->BlkSize) {
 | 
						|
	Completed = TRUE;
 | 
						|
    Instance->LastBlock = Block;
 | 
						|
    Mtftp4SetLastBlockNum (&Instance->Blocks, Block);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Remove this block number from the file hole. If Mtftp4RemoveBlockNum
 | 
						|
  // returns EFI_NOT_FOUND, the block has been saved, don't save it again.
 | 
						|
  // Note that : For bigger files, allowing the block counter to roll over
 | 
						|
  // to accept transfers of unlimited size. So TotalBlock is memorised as 
 | 
						|
  // continuous block counter.
 | 
						|
  //
 | 
						|
  Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block, Completed, &TotalBlock);
 | 
						|
 | 
						|
  if (Status == EFI_NOT_FOUND) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  } else if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Token->CheckPacket != NULL) {
 | 
						|
    Status = Token->CheckPacket (&Instance->Mtftp4, Token, (UINT16) Len, Packet);
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      Mtftp4SendError (
 | 
						|
        Instance,
 | 
						|
        EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
 | 
						|
        (UINT8 *) "User aborted download"
 | 
						|
        );
 | 
						|
 | 
						|
      return EFI_ABORTED;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (Token->Buffer != NULL) {
 | 
						|
     Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize);
 | 
						|
 | 
						|
    if (Start + DataLen <= Token->BufferSize) {
 | 
						|
      CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);
 | 
						|
 | 
						|
      //
 | 
						|
      // Update the file size when received the last block
 | 
						|
      //
 | 
						|
      if ((Instance->LastBlock == Block) && Completed) {
 | 
						|
        Token->BufferSize = Start + DataLen;
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (Instance->LastBlock != 0) {
 | 
						|
      //
 | 
						|
      // Don't save the data if the buffer is too small, return
 | 
						|
      // EFI_BUFFER_TOO_SMALL if received the last packet. This
 | 
						|
      // will give a accurate file length.
 | 
						|
      //
 | 
						|
      Token->BufferSize = Start + DataLen;
 | 
						|
 | 
						|
      Mtftp4SendError (
 | 
						|
        Instance,
 | 
						|
        EFI_MTFTP4_ERRORCODE_DISK_FULL,
 | 
						|
        (UINT8 *) "User provided memory block is too small"
 | 
						|
        );
 | 
						|
 | 
						|
      return EFI_BUFFER_TOO_SMALL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Function to process the received data packets. 
 | 
						|
  
 | 
						|
  It will save the block then send back an ACK if it is active.
 | 
						|
 | 
						|
  @param  Instance              The downloading MTFTP session
 | 
						|
  @param  Packet                The packet received
 | 
						|
  @param  Len                   The length of the packet
 | 
						|
  @param  Multicast             Whether this packet is multicast or unicast
 | 
						|
  @param  Completed             Return whether the download has completed
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The data packet is successfully processed
 | 
						|
  @retval EFI_ABORTED           The download is aborted by the user
 | 
						|
  @retval EFI_BUFFER_TOO_SMALL  The user provided buffer is too small
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Mtftp4RrqHandleData (
 | 
						|
  IN     MTFTP4_PROTOCOL       *Instance,
 | 
						|
  IN     EFI_MTFTP4_PACKET     *Packet,
 | 
						|
  IN     UINT32                Len,
 | 
						|
  IN     BOOLEAN               Multicast,
 | 
						|
     OUT BOOLEAN               *Completed
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  UINT16                    BlockNum;
 | 
						|
  INTN                      Expected;
 | 
						|
 | 
						|
  *Completed  = FALSE;
 | 
						|
  BlockNum    = NTOHS (Packet->Data.Block);
 | 
						|
  Expected    = Mtftp4GetNextBlockNum (&Instance->Blocks);
 | 
						|
 | 
						|
  ASSERT (Expected >= 0);
 | 
						|
 | 
						|
  //
 | 
						|
  // If we are active and received an unexpected packet, retransmit
 | 
						|
  // the last ACK then restart receiving. If we are passive, save
 | 
						|
  // the block.
 | 
						|
  //
 | 
						|
  if (Instance->Master && (Expected != BlockNum)) {
 | 
						|
    Mtftp4Retransmit (Instance);
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = Mtftp4RrqSaveBlock (Instance, Packet, Len);
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Reset the passive client's timer whenever it received a
 | 
						|
  // valid data packet.
 | 
						|
  //
 | 
						|
  if (!Instance->Master) {
 | 
						|
    Mtftp4SetTimeout (Instance);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check whether we have received all the blocks. Send the ACK if we
 | 
						|
  // are active (unicast client or master client for multicast download).
 | 
						|
  // If we have received all the blocks, send an ACK even if we are passive
 | 
						|
  // to tell the server that we are done.
 | 
						|
  //
 | 
						|
  Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
 | 
						|
 | 
						|
  if (Instance->Master || (Expected < 0)) {
 | 
						|
    if (Expected < 0) {
 | 
						|
      //
 | 
						|
      // If we are passive client, then the just received Block maybe
 | 
						|
      // isn't the last block. We need to send an ACK to the last block
 | 
						|
      // to inform the server that we are done. If we are active client,
 | 
						|
      // the Block == Instance->LastBlock.
 | 
						|
      //
 | 
						|
      BlockNum   = Instance->LastBlock;
 | 
						|
      *Completed = TRUE;
 | 
						|
 | 
						|
    } else {
 | 
						|
      BlockNum = (UINT16) (Expected - 1);
 | 
						|
    }
 | 
						|
 | 
						|
    Mtftp4RrqSendAck (Instance, BlockNum);
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Validate whether the options received in the server's OACK packet is valid.
 | 
						|
  
 | 
						|
  The options are valid only if:
 | 
						|
  1. The server doesn't include options not requested by us
 | 
						|
  2. The server can only use smaller blksize than that is requested
 | 
						|
  3. The server can only use the same timeout as requested
 | 
						|
  4. The server doesn't change its multicast channel.
 | 
						|
 | 
						|
  @param  This                  The downloading Mtftp session
 | 
						|
  @param  Reply                 The options in the OACK packet
 | 
						|
  @param  Request               The requested options
 | 
						|
 | 
						|
  @retval TRUE                  The options in the OACK is OK.
 | 
						|
  @retval FALSE                 The options in the OACK is invalid.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
Mtftp4RrqOackValid (
 | 
						|
  IN MTFTP4_PROTOCOL        *This,
 | 
						|
  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;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // The server can send ",,master" to client to change its master
 | 
						|
  // setting. But if it use the specific multicast channel, it can't
 | 
						|
  // change the setting.
 | 
						|
  //
 | 
						|
  if (((Reply->Exist & MTFTP4_MCAST_EXIST) != 0) && (This->McastIp != 0)) {
 | 
						|
    if ((Reply->McastIp != 0) && (Reply->McastIp != This->McastIp)) {
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((Reply->McastPort != 0) && (Reply->McastPort != This->McastPort)) {
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Configure a UDP IO port to receive the multicast.
 | 
						|
 | 
						|
  @param  McastIo               The UDP IO to configure
 | 
						|
  @param  Context               The opaque parameter to the function which is the
 | 
						|
                                MTFTP session.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The UDP child is successfully configured.
 | 
						|
  @retval Others                Failed to configure the UDP child.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
Mtftp4RrqConfigMcastPort (
 | 
						|
  IN UDP_IO                 *McastIo,
 | 
						|
  IN VOID                   *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  MTFTP4_PROTOCOL           *Instance;
 | 
						|
  EFI_MTFTP4_CONFIG_DATA    *Config;
 | 
						|
  EFI_UDP4_CONFIG_DATA      UdpConfig;
 | 
						|
  EFI_IPv4_ADDRESS          Group;
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  IP4_ADDR                  Ip;
 | 
						|
 | 
						|
  Instance                     = (MTFTP4_PROTOCOL *) Context;
 | 
						|
  Config                       = &Instance->Config;
 | 
						|
 | 
						|
  UdpConfig.AcceptBroadcast    = FALSE;
 | 
						|
  UdpConfig.AcceptPromiscuous  = FALSE;
 | 
						|
  UdpConfig.AcceptAnyPort      = FALSE;
 | 
						|
  UdpConfig.AllowDuplicatePort = FALSE;
 | 
						|
  UdpConfig.TypeOfService      = 0;
 | 
						|
  UdpConfig.TimeToLive         = 64;
 | 
						|
  UdpConfig.DoNotFragment      = FALSE;
 | 
						|
  UdpConfig.ReceiveTimeout     = 0;
 | 
						|
  UdpConfig.TransmitTimeout    = 0;
 | 
						|
  UdpConfig.UseDefaultAddress  = Config->UseDefaultSetting;
 | 
						|
  IP4_COPY_ADDRESS (&UdpConfig.StationAddress, &Config->StationIp);
 | 
						|
  IP4_COPY_ADDRESS (&UdpConfig.SubnetMask, &Config->SubnetMask);
 | 
						|
  UdpConfig.StationPort        = Instance->McastPort;
 | 
						|
  UdpConfig.RemotePort         = 0;
 | 
						|
 | 
						|
  Ip = HTONL (Instance->ServerIp);
 | 
						|
  IP4_COPY_ADDRESS (&UdpConfig.RemoteAddress, &Ip);
 | 
						|
 | 
						|
  Status = McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, &UdpConfig);
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!Config->UseDefaultSetting && 
 | 
						|
      !EFI_IP4_EQUAL (&mZeroIp4Addr, &Config->GatewayIp)) {
 | 
						|
    //
 | 
						|
    // The station IP address is manually configured and the Gateway IP is not 0.
 | 
						|
    // Add the default route for this UDP instance.
 | 
						|
    //
 | 
						|
    Status = McastIo->Protocol.Udp4->Routes (
 | 
						|
                                       McastIo->Protocol.Udp4, 
 | 
						|
                                       FALSE,
 | 
						|
                                       &mZeroIp4Addr,
 | 
						|
                                       &mZeroIp4Addr,
 | 
						|
                                       &Config->GatewayIp
 | 
						|
                                       );
 | 
						|
                             
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, NULL);
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // join the multicast group
 | 
						|
  //
 | 
						|
  Ip = HTONL (Instance->McastIp);
 | 
						|
  IP4_COPY_ADDRESS (&Group, &Ip);
 | 
						|
 | 
						|
  return McastIo->Protocol.Udp4->Groups (McastIo->Protocol.Udp4, TRUE, &Group);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Function to process the OACK. 
 | 
						|
  
 | 
						|
  It will first validate the OACK packet, then update the various negotiated parameters.
 | 
						|
 | 
						|
  @param  Instance              The download MTFTP session
 | 
						|
  @param  Packet                The packet received
 | 
						|
  @param  Len                   The packet length
 | 
						|
  @param  Multicast             Whether this packet is received as a multicast
 | 
						|
  @param  Completed             Returns whether the download has completed. NOT
 | 
						|
                                used  by this function.
 | 
						|
 | 
						|
  @retval EFI_DEVICE_ERROR      Failed to create/start a multicast UDP child
 | 
						|
  @retval EFI_TFTP_ERROR        Some error happened during the process
 | 
						|
  @retval EFI_SUCCESS           The OACK is successfully processed.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Mtftp4RrqHandleOack (
 | 
						|
  IN OUT MTFTP4_PROTOCOL       *Instance,
 | 
						|
  IN     EFI_MTFTP4_PACKET     *Packet,
 | 
						|
  IN     UINT32                Len,
 | 
						|
  IN     BOOLEAN               Multicast,
 | 
						|
     OUT BOOLEAN               *Completed
 | 
						|
  )
 | 
						|
{
 | 
						|
  MTFTP4_OPTION             Reply;
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  INTN                      Expected;
 | 
						|
  EFI_UDP4_PROTOCOL         *Udp4;
 | 
						|
 | 
						|
  *Completed = FALSE;
 | 
						|
 | 
						|
  //
 | 
						|
  // If already started the master download, don't change the
 | 
						|
  // setting. Master download always succeeds.
 | 
						|
  //
 | 
						|
  Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
 | 
						|
  ASSERT (Expected != -1);
 | 
						|
 | 
						|
  if (Instance->Master && (Expected != 1)) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Parse and validate the options from server
 | 
						|
  //
 | 
						|
  ZeroMem (&Reply, sizeof (MTFTP4_OPTION));
 | 
						|
 | 
						|
  Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);
 | 
						|
 | 
						|
  if (EFI_ERROR (Status) ||
 | 
						|
      !Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) {
 | 
						|
    //
 | 
						|
    // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.
 | 
						|
    //
 | 
						|
    if (Status != EFI_OUT_OF_RESOURCES) {
 | 
						|
      Mtftp4SendError (
 | 
						|
        Instance,
 | 
						|
        EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
 | 
						|
        (UINT8 *) "Mal-formated OACK packet"
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    return EFI_TFTP_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((Reply.Exist & MTFTP4_MCAST_EXIST) != 0) {
 | 
						|
 | 
						|
    //
 | 
						|
    // Save the multicast info. Always update the Master, only update the
 | 
						|
    // multicast IP address, block size, timeoute at the first time. If IP
 | 
						|
    // address is updated, create a UDP child to receive the multicast.
 | 
						|
    //
 | 
						|
    Instance->Master = Reply.Master;
 | 
						|
 | 
						|
    if (Instance->McastIp == 0) {
 | 
						|
      if ((Reply.McastIp == 0) || (Reply.McastPort == 0)) {
 | 
						|
        Mtftp4SendError (
 | 
						|
          Instance,
 | 
						|
          EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
 | 
						|
          (UINT8 *) "Illegal multicast setting"
 | 
						|
          );
 | 
						|
 | 
						|
        return EFI_TFTP_ERROR;
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // Create a UDP child then start receive the multicast from it.
 | 
						|
      //
 | 
						|
      Instance->McastIp      = Reply.McastIp;
 | 
						|
      Instance->McastPort    = Reply.McastPort;
 | 
						|
      if (Instance->McastUdpPort == NULL) {
 | 
						|
        Instance->McastUdpPort = UdpIoCreateIo (
 | 
						|
                                   Instance->Service->Controller,
 | 
						|
                                   Instance->Service->Image,
 | 
						|
                                   Mtftp4RrqConfigMcastPort,
 | 
						|
                                   UDP_IO_UDP4_VERSION,
 | 
						|
                                   Instance
 | 
						|
                                   );
 | 
						|
        if (Instance->McastUdpPort != NULL) {
 | 
						|
          Status = gBS->OpenProtocol (
 | 
						|
                          Instance->McastUdpPort->UdpHandle,
 | 
						|
                          &gEfiUdp4ProtocolGuid,
 | 
						|
                          (VOID **) &Udp4,
 | 
						|
                          Instance->Service->Image,
 | 
						|
                          Instance->Handle,
 | 
						|
                          EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
 | 
						|
                          );
 | 
						|
          if (EFI_ERROR (Status)) {
 | 
						|
            UdpIoFreeIo (Instance->McastUdpPort);
 | 
						|
            Instance->McastUdpPort = NULL;
 | 
						|
            return EFI_DEVICE_ERROR;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
 | 
						|
      if (Instance->McastUdpPort == NULL) {
 | 
						|
        return EFI_DEVICE_ERROR;
 | 
						|
      }
 | 
						|
 | 
						|
      Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
 | 
						|
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        Mtftp4SendError (
 | 
						|
          Instance,
 | 
						|
          EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION,
 | 
						|
          (UINT8 *) "Failed to create socket to receive multicast packet"
 | 
						|
          );
 | 
						|
 | 
						|
        return Status;
 | 
						|
      }
 | 
						|
    
 | 
						|
      //
 | 
						|
      // Update the parameters used.
 | 
						|
      //
 | 
						|
      if (Reply.BlkSize != 0) {
 | 
						|
        Instance->BlkSize = Reply.BlkSize;
 | 
						|
      }
 | 
						|
      
 | 
						|
      if (Reply.Timeout != 0) {
 | 
						|
        Instance->Timeout = Reply.Timeout;
 | 
						|
      }  
 | 
						|
    }    
 | 
						|
    
 | 
						|
  } else {
 | 
						|
    Instance->Master = TRUE;
 | 
						|
    
 | 
						|
    if (Reply.BlkSize != 0) {
 | 
						|
      Instance->BlkSize = Reply.BlkSize;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Reply.Timeout != 0) {
 | 
						|
      Instance->Timeout = Reply.Timeout;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  //
 | 
						|
  // Send an ACK to (Expected - 1) which is 0 for unicast download,
 | 
						|
  // or tell the server we want to receive the Expected block.
 | 
						|
  //
 | 
						|
  return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  The packet process callback for MTFTP download.
 | 
						|
 | 
						|
  @param  UdpPacket             The packet received
 | 
						|
  @param  EndPoint              The local/remote access point of the packet
 | 
						|
  @param  IoStatus              The status of the receiving
 | 
						|
  @param  Context               Opaque parameter, which is the MTFTP session
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
Mtftp4RrqInput (
 | 
						|
  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;
 | 
						|
  BOOLEAN                   Multicast;
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  UINT16                    Opcode;
 | 
						|
  UINT32                    Len;
 | 
						|
 | 
						|
  Instance  = (MTFTP4_PROTOCOL *) Context;
 | 
						|
  NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
 | 
						|
 | 
						|
  Status    = EFI_SUCCESS;
 | 
						|
  Packet    = NULL;
 | 
						|
  Completed = FALSE;
 | 
						|
  Multicast = FALSE;
 | 
						|
 | 
						|
  if (EFI_ERROR (IoStatus)) {
 | 
						|
    Status = IoStatus;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  ASSERT (UdpPacket != NULL);
 | 
						|
 | 
						|
  //
 | 
						|
  // Find the port this packet is from to restart receive correctly.
 | 
						|
  //
 | 
						|
  Multicast = (BOOLEAN) (EndPoint->LocalAddr.Addr[0] == Instance->McastIp);
 | 
						|
 | 
						|
  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. The server
 | 
						|
  // is required to use the same port as RemotePort to multicast the
 | 
						|
  // data.
 | 
						|
  //
 | 
						|
  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_DATA:
 | 
						|
    if ((Len > (UINT32) (MTFTP4_DATA_HEAD_LEN + Instance->BlkSize)) ||
 | 
						|
        (Len < (UINT32) MTFTP4_DATA_HEAD_LEN)) {
 | 
						|
      goto ON_EXIT;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = Mtftp4RrqHandleData (Instance, Packet, Len, Multicast, &Completed);
 | 
						|
    break;
 | 
						|
 | 
						|
  case EFI_MTFTP4_OPCODE_OACK:
 | 
						|
    if (Multicast || (Len <= MTFTP4_OPCODE_LEN)) {
 | 
						|
      goto ON_EXIT;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = Mtftp4RrqHandleOack (Instance, Packet, Len, Multicast, &Completed);
 | 
						|
    break;
 | 
						|
 | 
						|
  case EFI_MTFTP4_OPCODE_ERROR:
 | 
						|
    Status = EFI_TFTP_ERROR;
 | 
						|
    break;
 | 
						|
    
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
ON_EXIT:
 | 
						|
 | 
						|
  //
 | 
						|
  // Free the resources, then if !EFI_ERROR (Status), 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) {
 | 
						|
    if (Multicast) {
 | 
						|
      Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
 | 
						|
    } else {
 | 
						|
      Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (EFI_ERROR (Status) || Completed) {
 | 
						|
    Mtftp4CleanOperation (Instance, Status);
 | 
						|
  }
 | 
						|
}
 |