pxe_bc_mtftp.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 2,396 行 · 第 1/5 页

C
2,396
字号
        SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr);
      }

      return Status;
    }

    if (DontUseBuffer) {
      BufferSize += ReplyLen;
    } else {
      BufferPtr += ReplyLen;
      BufferSize -= ReplyLen;
    }
  }
  //
  // while (ReplyLen == PacketSize);
  //
  if (DontUseBuffer) {
    if (BufferSizePtr != NULL) {
      *BufferSizePtr = (BufferSize - PacketSize);
    }
  } else {
    *BufferSizePtr -= BufferSize;
  }

  /* Send ACK of last packet. */
  ReplyLen = 0;

  SendAckAndGetData (
    Private,
    ServerIpPtr,
    ServerPortPtr,
    ReplyIpPtr,
    OurPortPtr,
    Timeout,
    (UINTN *) &ReplyLen,
    BufferPtr,
    &BlockNum,
    TRUE
    );

  return EFI_SUCCESS;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// some literals
//
STATIC UINT8                      Mode[]          = MODE_BINARY;
STATIC UINT8                      BlockSizeOp[]   = OP_BLKSIZE;
STATIC UINT8                      TsizeOp[]       = OP_TFRSIZE;
STATIC UINT8                      OverwriteOp[]   = OP_OVERWRITE;
STATIC UINT8                      BigBlkNumOp[]   = OP_BIGBLKNUM;
STATIC EFI_PXE_BASE_CODE_UDP_PORT TftpRequestPort = TFTP_OPEN_PORT;

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
STATIC
UINT8 *
FindOption (
  UINT8 *OptionPtr,
  INTN  OpLen,
  UINT8 *OackPtr,
  INTN  OackSize
  )
/*++
Routine description:
  Check TFTP OACK packet for option.

Parameters:
  OptionPtr := Pointer to option string to find
  OpLen := Length of option string
  OackPtr := Pointer to OACK data
  OackSize := Length of OACK data

Returns:
  Pointer to value field if option found or NULL if not found.
--*/
{
  if ((OackSize -= OpLen) <= 0) {
    return NULL;
  }

  do {
    if (!EfiCompareMem (OackPtr, OptionPtr, OpLen)) {
      return OackPtr + OpLen;
    }

    ++OackPtr;
  } while (--OackSize);

  return NULL;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define BKSZOP      1 // block size
#define TSIZEOP     2 // transfer size
#define OVERWRITEOP 4 // overwrite
#define BIGBLKNUMOP 8 // big block numbers
STATIC
EFI_STATUS
TftpRwReq (
  UINT16                      Req,
  UINT16                      Options,
  PXE_BASECODE_DEVICE         *Private,
  EFI_IP_ADDRESS              *ServerIpPtr,
  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,
  EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,
  UINT8                       *FilenamePtr,
  UINTN                       *PacketSizePtr,
  VOID                        *Buffer
  )
/*++
Routine description:
  Send TFTP RRQ/WRQ packet.

Parameters:
  Req := Type of request to send
  Options := One or more of the #define values above
  Private := Pointer to PxeBc interface
  ServerIpPtr := Pointer to TFTP server IP address
  ServerPortPtr := Pointer to TFTP server UDP port
  OurPortPtr := Pointer to TFTP client UDP port
  FilenamePtr := Pointer to TFTP file or directory name
  PacketSizePtr := Pointer to block size
  Buffer := 

Returns:
--*/
{
  union {
    UINT8             Data[514];
    struct Tftpv4Req  ReqStr;
  } *u;

  UINT16  OpFlags;
  INTN    Len;
  INTN    TotalLen;
  UINT8   *Ptr;

  if (*OurPortPtr == 0) {
    OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT;
  } else {
    OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT;
  }
  //
  // build the basic request - opcode, filename, mode
  //
  u                 = Buffer;
  u->ReqStr.OpCode  = HTONS (Req);
  TotalLen = sizeof (Mode) + sizeof (u->ReqStr.OpCode) + (Len = 1 + EfiAsciiStrLen (FilenamePtr));

  EfiCopyMem (u->ReqStr.FileName, FilenamePtr, Len);
  Ptr = (UINT8 *) (u->ReqStr.FileName + Len);

  EfiCopyMem (Ptr, Mode, sizeof (Mode));
  Ptr += sizeof (Mode);

  if (Options & BKSZOP) {
    EfiCopyMem (Ptr, BlockSizeOp, sizeof (BlockSizeOp));
    UtoA10 (*PacketSizePtr, Ptr + sizeof (BlockSizeOp));

    TotalLen += (Len = 1 + EfiAsciiStrLen (Ptr + sizeof (BlockSizeOp)) + sizeof (BlockSizeOp));

    Ptr += Len;
  }

  if (Options & TSIZEOP) {
    EfiCopyMem (Ptr, TsizeOp, sizeof (TsizeOp));
    EfiCopyMem (Ptr + sizeof (TsizeOp), "0", 2);
    TotalLen += sizeof (TsizeOp) + 2;
    Ptr += sizeof (TsizeOp) + 2;
  }

  if (Options & OVERWRITEOP) {
    EfiCopyMem (Ptr, OverwriteOp, sizeof (OverwriteOp));
    EfiCopyMem (Ptr + sizeof (OverwriteOp), "1", 2);
    TotalLen += sizeof (OverwriteOp) + 2;
    Ptr += sizeof (OverwriteOp) + 2;
  }

  if (Options & BIGBLKNUMOP) {
    EfiCopyMem (Ptr, BigBlkNumOp, sizeof (BigBlkNumOp));
    EfiCopyMem (Ptr + sizeof (BigBlkNumOp), "8", 2);
    TotalLen += sizeof (BigBlkNumOp) + 2;
    Ptr += sizeof (BigBlkNumOp) + 2;
  }
  //
  // send it
  //
  return UdpWrite (
          Private,
          OpFlags,
          ServerIpPtr,
          ServerPortPtr,
          0,
          0,
          OurPortPtr,
          0,
          0,
          (UINTN *) &TotalLen,
          u
          );
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
STATIC
EFI_STATUS
TftpRwReqwResp (
  UINT16                      Req,
  UINT16                      Options,
  PXE_BASECODE_DEVICE         *Private,
  VOID                        *HeaderPtr,
  UINTN                       *PacketSizePtr,
  UINTN                       *ReplyLenPtr,
  VOID                        *BufferPtr,
  EFI_IP_ADDRESS              *ServerIpPtr,
  EFI_PXE_BASE_CODE_UDP_PORT  *ServerPortPtr,
  EFI_PXE_BASE_CODE_UDP_PORT  *ServerReplyPortPtr,
  EFI_PXE_BASE_CODE_UDP_PORT  *OurPortPtr,
  UINT8                       *FilenamePtr,
  UINT16                      Timeout
  )
/*++
Routine description:
  Start TFTP session.  Issue request and wait for response.
  Retry three times on error.  If failed using options,
  retry three times w/o options on error.

Parameters:
  Req := TFTP request type
  Options := TFTP option bits
  Private := Pointer to PxeBc interface
  HeaderPtr := 
  PacketSizePtr := Pointer to block size
  ReplyLenPtr := 
  BufferPtr := 
  ServerIpPtr := Pointer to TFTP server IP address
  ServerPortPtr := Pointer to TFTP server UDP port
  ServerReplyPortPtr := 
  OurPortPtr := Pointer to TFTP client UDP Port
  FilenamePtr := Pointer to file or directory name
  Timeout := 

Returns:
--*/
{
  EFI_STATUS  Status;
  UINTN       SaveReplyLen;
  INTN        Retries;
  UINT8       Buffer[514];

  SaveReplyLen            = *ReplyLenPtr;
  Retries                 = 3;
  Private->BigBlkNumFlag  = FALSE;
  *OurPortPtr             = 0;
  //
  // generate random
  //
  do {
    if (*OurPortPtr != 0) {
      if (++ *OurPortPtr == 0) {
        *OurPortPtr = PXE_RND_PORT_LOW;
      }
    }
    //
    // send request from our Ip = StationIp
    //
    if ((Status = TftpRwReq (
                    Req,
                    Options,
                    Private,
                    ServerIpPtr,
                    ServerPortPtr,
                    OurPortPtr,
                    FilenamePtr,
                    PacketSizePtr,
                    Buffer
                    )) != EFI_SUCCESS) {
      DEBUG (
        (EFI_D_WARN,
        "\nTftpRwReqwResp()  Exit #1  %xh (%r)",
        Status,
        Status)
        );

      return Status;
    }
    //
    // read reply to our Ip = StationIp
    //
    *ReplyLenPtr = SaveReplyLen;

    Status = TftpUdpRead (
              Private,
              EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT,
              HeaderPtr,
              ReplyLenPtr,
              BufferPtr,
              ServerIpPtr,
              ServerReplyPortPtr,
              0,
              OurPortPtr,
              Timeout
              );
  } while (Status == EFI_TIMEOUT && --Retries);

  if (!Options || Status != EFI_TFTP_ERROR) {
    DEBUG (
      (EFI_D_WARN,
      "\nTftpRwReqwResp()  Exit #2  %xh (%r)",
      Status,
      Status)
      );
    return Status;
  }

  Status = TftpRwReqwResp (
            Req,
            0,
            Private,
            HeaderPtr,
            PacketSizePtr,
            ReplyLenPtr,
            BufferPtr,
            ServerIpPtr,
            ServerPortPtr,
            ServerReplyPortPtr,
            OurPortPtr,
            FilenamePtr,
            Timeout
            );

  DEBUG ((EFI_D_WARN, "\nTftpRwReqwResp()  Exit #3  %xh (%r)", Status, Status));

  return Status;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

//
// mtftp listen
// read on mcast ip, cport, from sport, for data packet
// returns success if gets multicast last packet or all up to last block
// if not missing, then finished
//
STATIC
EFI_STATUS
MtftpListen (
  PXE_BASECODE_DEVICE           *Private,
  UINT64                        *BufferSizePtr,
  UINT8                         *BufferPtr,
  EFI_IP_ADDRESS                *ServerIpPtr,
  EFI_PXE_BASE_CODE_MTFTP_INFO  *MtftpInfoPtr,
  UINT64                        *StartBlockPtr,
  UINTN                         *NumMissedPtr,
  UINT16                        TransTimeout,
  UINT16                        ListenTimeout,
  UINT64                        FinalBlock,
  IN BOOLEAN                    DontUseBuffer
  )
/*++
Routine description:
  Listen for MTFTP traffic and save desired packets.

Parameters:
  Private := Pointer to PxeBc interface
  BufferSizePtr := 
  BufferPtr := 
  ServerIpPtr := Pointer to TFTP server IP address
  MtftpInfoPtr := Pointer to MTFTP session information
  StartBlockPtr := IN=first block we are looking for  OUT=first block received
  NumMissedPtr := Number of blocks missed
  TransTimeout := 
  ListenTimeout := 
  FinalBlock := 
  DontUseBuffer := TRUE == throw packets away, just count bytes

Returns:
--*/
{
  EFI_STATUS        Status;
  struct Tftpv4Ack  Header;
  UINT64            Offset;
  UINT64            BlockNum;
  UINT64            LastBlockNum;
  UINT64            BufferSize;
  UINTN             NumMissed;
  UINTN             PacketSize;
  UINTN             SaveReplyLen;
  UINTN             ReplyLen;
  UINT16            Timeout;

  LastBlockNum  = *StartBlockPtr;
  Timeout       = ListenTimeout;
  *NumMissedPtr = 0;
  PacketSize    = 0;
  BufferSize    = *BufferSizePtr;
  ReplyLen      = MAX_TFTP_PKT_SIZE;;

  //
  // receive
  //
  do {
    if ((SaveReplyLen = ReplyLen) > BufferSize) {
      if ((SaveReplyLen = (UINTN) BufferSize) < 0) {
        //
        // make sure we do not overrun buffer
        //
        SaveReplyLen = 0;
      }
    }

    /* %%TBD - add big block number support */

    //
    // get data - loop on resends
    //
    do {
      ReplyLen = SaveReplyLen;

      if ((Status = TftpUdpRead (
                      Private,
                      0,
                      &Header,
                      &ReplyLen,
                      BufferPtr,
                      ServerIpPtr,
                      &MtftpInfoPtr->SPort,
                      &MtftpInfoPtr->MCastIp,
                      &MtftpInfoPtr->CPort,
                      Timeout
                      )) != EFI_SUCCESS) {
        return Status;
      }
      //
      // make sure a data packet
      //
      if (Header.OpCode != HTONS (TFTP_DATA)) {
        return EFI_PROTOCOL_ERROR;
      }
    } while ((BlockNum = NTOHS (Header.BlockNum)) == LastBlockNum);

    //
    // make sure still going up
    //
    if (LastBlockNum > BlockNum) {
      return EFI_PROTOCOL_ERROR;
    }

    if (BlockNum - LastBlockNum > 0xFFFFFFFF) {
      return EFI_PROTOCOL_ERROR;
    } else {
      NumMissed = (UINTN) (BlockNum - LastBlockNum - 1);
    }

    LastBlockNum = BlockNum;

    //
    // if first time through, some reinitialization
    //
    if (!PacketSize) {
      *StartBlockPtr  = BlockNum;
      PacketSize      = ReplyLen;
      Timeout         = TransTimeout;
    } else {
      *NumMissedPtr = (UINT16) (*NumMissedPtr + NumMissed);
    }
    //
    // if missed packets, update start block,
    // etc. and move packet to proper place in buffer
    //
    if (NumMissed) {
      *StartBlockPtr = BlockNum;
      if (!DontUseBuffer) {
        Offset = NumMissed * PacketSize;
        EfiCopyMem (BufferPtr + Offset, BufferPtr, ReplyLen);
        BufferPtr += Offset;
        BufferSize -= Offset;
      }
    }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?