mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-25 11:55:28 +01:00
15dada20e6
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
1241 lines
33 KiB
C
1241 lines
33 KiB
C
/** @file
|
|
Mtftp6 support functions implementation.
|
|
|
|
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#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] BlockCounter The continuous block counter instead of the value after roll-over.
|
|
|
|
@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 *BlockCounter
|
|
)
|
|
{
|
|
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.
|
|
//
|
|
*BlockCounter = Num;
|
|
|
|
if (Range->Round > 0) {
|
|
*BlockCounter += 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->Operation = 0;
|
|
Instance->WindowSize = 1;
|
|
Instance->TotalBlock = 0;
|
|
Instance->AckedBlock = 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);
|
|
|
|
Instance->Operation = OpCode;
|
|
|
|
//
|
|
// Parse the extension options in the request packet.
|
|
//
|
|
if (Token->OptionCount != 0) {
|
|
|
|
Status = Mtftp6ParseExtensionOption (
|
|
Token->OptionList,
|
|
Token->OptionCount,
|
|
TRUE,
|
|
Instance->Operation,
|
|
&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->WindowSize == 0) {
|
|
Instance->WindowSize = MTFTP6_DEFAULT_WINDOWSIZE;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|