【UEFI基础】EDK网络框架(通用函数和数据)

通用函数和数据

DPC

DPC全称Deferred Procedure Call。Deferred的意思是“延迟”,这个DPC的作用就是注册函数,然后在之后的某个时刻调用,所以确实是有“延迟”的意思。DPC在UEFI的实现中包括两个部分。一部分是库函数DxeDpcLib,对应代码NetworkPkg\Library\DxeDpcLib\DxeDpcLib.inf;另一部分是EFI_DPC_PROTOCOL,对应代码NetworkPkg\DpcDxe\DpcDxe.inf。

DxeDpcLib

库中只实现了两个函数:

/**
  Add a Deferred Procedure Call to the end of the DPC queue.
  @param[in]  DpcTpl        The EFI_TPL that the DPC should be invoked.
  @param[in]  DpcProcedure  Pointer to the DPC's function.
  @param[in]  DpcContext    Pointer to the DPC's context.  Passed to DpcProcedure
                            when DpcProcedure is invoked.
  @retval EFI_SUCCESS            The DPC was queued.
  @retval EFI_INVALID_PARAMETER  DpcTpl is not a valid EFI_TPL.
  @retval EFI_INVALID_PARAMETER  DpcProcedure is NULL.
  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to
                                 add the DPC to the queue.
**/
EFI_STATUS
EFIAPI
QueueDpc (
  IN EFI_TPL            DpcTpl,
  IN EFI_DPC_PROCEDURE  DpcProcedure,
  IN VOID               *DpcContext    OPTIONAL
  );

/**
  Dispatch the queue of DPCs.  ALL DPCs that have been queued with a DpcTpl
  value greater than or equal to the current TPL are invoked in the order that
  they were queued.  DPCs with higher DpcTpl values are invoked before DPCs with
  lower DpcTpl values.
  @retval EFI_SUCCESS    One or more DPCs were invoked.
  @retval EFI_NOT_FOUND  No DPCs were invoked.
**/
EFI_STATUS
EFIAPI
DispatchDpc (
  VOID
  );

库函数的实现只是简单调用了EFI_DPC_PROTOCOL的接口函数:

EFI_STATUS
EFIAPI
QueueDpc (
  IN EFI_TPL            DpcTpl,
  IN EFI_DPC_PROCEDURE  DpcProcedure,
  IN VOID               *DpcContext    OPTIONAL
  )
{
  //
  // Call the EFI_DPC_PROTOCOL to queue the DPC
  //
  return mDpc->QueueDpc (mDpc, DpcTpl, DpcProcedure, DpcContext);
}

我们在使用DPC的时候直接调用库函数更方便,不过在了解DPC的实现时还是要重点关注EFI_DPC_PROTOCOL

EFI_DPC_PROTOCOL

接口初始化

EFI_DPC_PROTOCOL在一个DXE模块中安装的,安装的过程也很简单,查看具体的模块入口代码(位于NetworkPkg\DpcDxe\Dpc.c,只包含重点代码,下同):

EFI_STATUS
EFIAPI
DpcDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Initialize the DPC queue for all possible TPL values
  //
  for (Index = 0; Index <= TPL_HIGH_LEVEL; Index++) {
    InitializeListHead (&mDpcQueue[Index]);
  }
 
  //
  // Install the EFI_DPC_PROTOCOL instance onto a new handle
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &mDpcHandle,
                  &gEfiDpcProtocolGuid, 
                  &mDpc,
                  NULL
                  );
}

这里唯一需要关注的是mDpcQueue这个全局变量。它是一个链表数组:

//
// An array of DPC queues.  A DPC queue is allocated for every level EFI_TPL value.
// As DPCs are queued, they are added to the end of the linked list.
// As DPCs are dispatched, they are removed from the beginning of the linked list.
//
LIST_ENTRY  mDpcQueue[TPL_HIGH_LEVEL + 1];	// TPL_HIGH_LEVEL的值是31,所以数组大小是32个元素

每一个数组元素对应一种优先级。这里创建了所有可能的优先级,但实际上常用的也就下面几种:

//
// Task priority level
//
#define TPL_APPLICATION       4
#define TPL_CALLBACK          8
#define TPL_NOTIFY            16
#define TPL_HIGH_LEVEL        31

这个链表真正的有效元素是DpcEntry,其结构如下:

//
// Internal data structure for managing DPCs.  A DPC entry is either on the free
// list or on a DPC queue at a specific EFI_TPL.
//
typedef struct {
  LIST_ENTRY           ListEntry;
  EFI_DPC_PROCEDURE    DpcProcedure;
  VOID                 *DpcContext;
} DPC_ENTRY;

第一个参数ListEntry用来处理链表,可以不关注;后面的两个参数,一个是DPC函数指针,一个是DPC函数的入参。EFI_DPC_PROCEDURE定义如下:

/**
  Invoke a Deferred Procedure Call.
  @param  DpcContext           The pointer to the Deferred Procedure Call's context,
                               which is implementation dependent.
**/
typedef
VOID
(EFIAPI *EFI_DPC_PROCEDURE)(
  IN VOID  *DpcContext
  );

这个函数就是我们的主角:Deferred Procedure Call,简称DPC

接口实现

EFI_DPC_PROTOCOL包含两个接口函数,下面具体介绍。

  • 首先介绍DpcQueueDpc()
EFI_STATUS
EFIAPI
DpcQueueDpc (
  IN EFI_DPC_PROTOCOL   *This,
  IN EFI_TPL            DpcTpl,
  IN EFI_DPC_PROCEDURE  DpcProcedure,
  IN VOID               *DpcContext    OPTIONAL
  )

该函数的操作流程如下:

  1. 判断优先级DpcTpl是否满足要求:
  //
  // Make sure DpcTpl is valid
  //
  if ((DpcTpl < TPL_APPLICATION) || (DpcTpl > TPL_HIGH_LEVEL)) {
    return EFI_INVALID_PARAMETER;
  }
  1. 判断mDpcEntryFreeList的状态。这个mDpcEntryFreeList是另外的一个链表:
//
// Free list of DPC entries.  As DPCs are queued, entries are removed from this
// free list.  As DPC entries are dispatched, DPC entries are added to the free list.
// If the free list is empty and a DPC is queued, the free list is grown by allocating
// an additional set of DPC entries.
//
LIST_ENTRY  mDpcEntryFreeList = INITIALIZE_LIST_HEAD_VARIABLE (mDpcEntryFreeList);

一开始它是空的,在首次使用时会创建64个空的DPC_ENTRY,其基本代码:

  //
  // Check to see if there are any entries in the DPC free list
  //
  if (IsListEmpty (&mDpcEntryFreeList)) {
    //
    // Add 64 DPC entries to the free list
    //
    for (Index = 0; Index < 64; Index++) {
      //
      // Allocate a new DPC entry
      //
      DpcEntry = AllocatePool (sizeof (DPC_ENTRY));
      //
      // Add the newly allocated DPC entry to the DPC free list
      //
      InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);
    }
  }

这里去掉了一系列的条件判断,只保留基本的操作,就是一个分配内存给DPC_ENTRY,然后放到mDpcEntryFreeList的过程,表示的是没有使用到的DPC。

  1. mDpcEntryFreeList中拿出一个DpcEntry,为它赋值DpcProcedureDpcContext,这么做之后DPC才真正生效了:
  //
  // Retrieve the first node from the free list of DPCs
  //
  DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcEntryFreeList));

  //
  // Remove the first node from the free list of DPCs
  //
  RemoveEntryList (&DpcEntry->ListEntry);

  //
  // Fill in the DPC entry with the DpcProcedure and DpcContext
  //
  DpcEntry->DpcProcedure = DpcProcedure;
  DpcEntry->DpcContext   = DpcContext;
  1. 将初始化好的DpcEntry插入到mDpcQueue中,mDpcQueueDepth全局变量的值加1,表示又多了一个DPC:
  //
  // Add the DPC entry to the end of the list for the specified DplTpl.
  //
  InsertTailList (&mDpcQueue[DpcTpl], &DpcEntry->ListEntry);

  //
  // Increment the measured DPC queue depth across all TPLs
  //
  mDpcQueueDepth++;

mDpcQueuemDpcEntryFreeList的关系大致如下:

在这里插入图片描述

这里预分配资源给mDpcEntryFreeList,以及mDpcQueuemDpcEntryFreeList之间的DPC转换使用,其目的主要是能够减少资源的反复分配导致的内存碎片化,也能够实现名字中的“延迟”一说,而且执行速度上也有保证。

  • 然后介绍DpcDispatchDpc()
EFI_STATUS
EFIAPI
DpcDispatchDpc (
  IN EFI_DPC_PROTOCOL  *This
  )

该函数的操作流程如下:

  1. 判断mDpcQueueDepth值是否大于0,如果大于0,表示有DPC可以被调用,才会有后续的操作。
  //
  // Check to see if there are 1 or more DPCs currently queued
  //
  if (mDpcQueueDepth > 0) {
  1. 遍历mDpcQueue,找到非空的链表节点,将它从mDpcQueue中删去,mDpcQueueDepth的值相应的减1。
    //
    // Loop from TPL_HIGH_LEVEL down to the current TPL value
    //
    for (Tpl = TPL_HIGH_LEVEL; Tpl >= OriginalTpl; Tpl--) {
      //
      // Check to see if the DPC queue is empty
      //
      while (!IsListEmpty (&mDpcQueue[Tpl])) {
        //
        // Retrieve the first DPC entry from the DPC queue specified by Tpl
        //
        DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcQueue[Tpl]));

        //
        // Remove the first DPC entry from the DPC queue specified by Tpl
        //
        RemoveEntryList (&DpcEntry->ListEntry);

        //
        // Decrement the measured DPC Queue Depth across all TPLs
        //
        mDpcQueueDepth--;

注意这里的优先级是从高到低来执行对应的DPC的,这也是符合UEFI规范的。

  1. 执行那个从mDpcQueue去除下来的DpcEntry对应的DpcProcedure
        //
        // Invoke the DPC passing in its context
        //
        (DpcEntry->DpcProcedure)(DpcEntry->DpcContext);
  1. 将从mDpcQueue去除下来的DpcEntry在放回到mDpcEntryFreeList中,之后就可以重复利用:
        //
        // Add the invoked DPC entry to the DPC free list
        //
        InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);

以上就是DpcDispatchDpc()的流程。注意一次DpcDispatchDpc会将mDpcQueue中的所有DPC都执行一遍。

DPC代码示例

DpcLib的使用示例大致如下:

  1. 创建事件(位于NetworkPkg\ArpDxe\ArpDriver.c):
  //
  // Create the event used in the RxToken.
  //
  Status = gBS->CreateEvent (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  ArpOnFrameRcvd,
                  ArpService,
                  &ArpService->RxToken.Event
                  );
  if (EFI_ERROR (Status)) {
    goto ERROR_EXIT;
  }
  1. 事件的实现(位于NetworkPkg\ArpDxe\ArpImpl.c):
VOID
EFIAPI
ArpOnFrameRcvd (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  //
  // Request ArpOnFrameRcvdDpc as a DPC at TPL_CALLBACK
  //
  QueueDpc (TPL_CALLBACK, ArpOnFrameRcvdDpc, Context);
}

这里就会调用QueueDpc()。这实际上很像是Linux中断处理中的上半部和下半部,上述的代码实际上是上半部,下半部就是执行DispatchDpc(),下面是一个示例(位于NetworkPkg\ArpDxe\ArpImpl.c):

UINTN
ArpAddressResolved (
  IN ARP_CACHE_ENTRY    *CacheEntry,
  IN ARP_INSTANCE_DATA  *Instance OPTIONAL,
  IN EFI_EVENT          UserEvent OPTIONAL
  )
{
  //
  // Dispatch the DPCs queued by the NotifyFunction of the Context->UserRequestEvent.
  //
  DispatchDpc ();

  return Count;
}

执行DispatchDpc()的位置需要根据实际的代码逻辑来确定,这个将在后续实际使用模块中进一步说明。

下面是自己写的一个使用DpcLib的简单示例,代码可以在BeniPkg\DynamicCommand\TestDynamicCommand\TestDpc.c中找到:

/**
  DPC function.

  @param  NA

  @retval  NA

**/
VOID
EFIAPI
DpcCallback (
  IN  VOID                          *Context
  )
{
  Print (L"DPC callback function\r\n");
}

/**
  Test DpcLib code.

  @param  NA

  @return  NA

**/
VOID
TestDpc (
  VOID
  )
{
  QueueDpc (TPL_CALLBACK, DpcCallback, NULL);
}

这里执行TestDpc()之后就会注册一个DPC函数,该函数也只是简单的打印信息而已,执行结果如下:

在这里插入图片描述

有几点需要注意:

  1. 首先代码中并没有直接调用DispatchDpc(),但是我们注册的DPC函数DpcCallback()还是被执行了,这是因为当前BIOS已经包含了UEFI网络协议栈,所以会在某个网络驱动的执行代码中调用DispatchDpc(),而前面也已经说过DispatchDpc()会调用所有的DPC函数,所以这里注册的DPC也会被调用。
  2. 其次是多次执行的结果稍有不同,主要是输出信息顺序的变化,这跟上面说明的原因也类似,网络驱动事件调用的时间点有随机的成分,这也导致了DpcCallback()执行时间的变化。

Token

BIOS网络协议中的数据基本都是通过Token的形式来处理的,它有很多种不同的描述方式,整体描述如下:

在这里插入图片描述

需要注意的是Token实际上的使用者并不是提供者本身,而是提供者的上层协议,比如MNP的Token使用者是ARP、IP4等使用MNP的上层协议。

Token中比较通用的一种是:

typedef struct {
  EFI_EVENT     Event;
  EFI_STATUS    Status;
  // 其它结构体
  union {
    XXX_DATA     *RxData;
    XXX_DATA    *TxData;
    // 其它结构体
  } Packet;
} YYY;

注意结构体中写的XXXYYY并不是真实的名称,在不同协议层下有不同的表示。当然也有其它的形式(图中深色部分),后面也会说明。下面将例举出所有的Token的具体格式。

EFI_MANAGED_NETWORK_COMPLETION_TOKEN

typedef struct {
  ///
  /// This Event will be signaled after the Status field is updated
  /// by the MNP. The type of Event must be
  /// EFI_NOTIFY_SIGNAL. The Task Priority Level (TPL) of
  /// Event must be lower than or equal to TPL_CALLBACK.
  ///
  EFI_EVENT     Event;
  ///
  /// The status that is returned to the caller at the end of the operation
  /// to indicate whether this operation completed successfully.
  ///
  EFI_STATUS    Status;
  union {
    ///
    /// When this token is used for receiving, RxData is a pointer to the EFI_MANAGED_NETWORK_RECEIVE_DATA.
    ///
    EFI_MANAGED_NETWORK_RECEIVE_DATA     *RxData;
    ///
    /// When this token is used for transmitting, TxData is a pointer to the EFI_MANAGED_NETWORK_TRANSMIT_DATA.
    ///
    EFI_MANAGED_NETWORK_TRANSMIT_DATA    *TxData;
  } Packet;
} EFI_MANAGED_NETWORK_COMPLETION_TOKEN;

IP_IO_IP_COMPLETION_TOKEN

typedef struct {
  ///
  /// This Event will be signaled after the Status field is updated
  /// by the EFI IPv4 Protocol driver. The type of Event must be
  /// EFI_NOTIFY_SIGNAL. The Task Priority Level (TPL) of
  /// Event must be lower than or equal to TPL_CALLBACK.
  ///
  EFI_EVENT     Event;
  ///
  /// The status that is returned to the caller at the end of the operation
  /// to indicate whether this operation completed successfully.
  ///
  EFI_STATUS    Status;
  union {
    ///
    /// When this token is used for receiving, RxData is a pointer to the EFI_IP4_RECEIVE_DATA.
    ///
    EFI_IP4_RECEIVE_DATA     *RxData;
    ///
    /// When this token is used for transmitting, TxData is a pointer to the EFI_IP4_TRANSMIT_DATA.
    ///
    EFI_IP4_TRANSMIT_DATA    *TxData;
  } Packet;
} EFI_IP4_COMPLETION_TOKEN;

typedef union {
  EFI_IP4_COMPLETION_TOKEN    Ip4Token;
  EFI_IP6_COMPLETION_TOKEN    Ip6Token;
} IP_IO_IP_COMPLETION_TOKEN;

TCP_IO_CONNECTION_TOKEN

typedef struct {
  EFI_EVENT     Event;
  EFI_STATUS    Status;
} EFI_TCP4_COMPLETION_TOKEN;

typedef struct {
  ///
  /// The Status in the CompletionToken will be set to one of
  /// the following values if the active open succeeds or an unexpected
  /// error happens:
  /// EFI_SUCCESS:              The active open succeeds and the instance's
  ///                           state is Tcp4StateEstablished.
  /// EFI_CONNECTION_RESET:     The connect fails because the connection is reset
  ///                           either by instance itself or the communication peer.
  /// EFI_CONNECTION_REFUSED:   The connect fails because this connection is initiated with
  ///                           an active open and the connection is refused.
  /// EFI_ABORTED:              The active open is aborted.
  /// EFI_TIMEOUT:              The connection establishment timer expires and
  ///                           no more specific information is available.
  /// EFI_NETWORK_UNREACHABLE:  The active open fails because
  ///                           an ICMP network unreachable error is received.
  /// EFI_HOST_UNREACHABLE:     The active open fails because an
  ///                           ICMP host unreachable error is received.
  /// EFI_PROTOCOL_UNREACHABLE: The active open fails
  ///                           because an ICMP protocol unreachable error is received.
  /// EFI_PORT_UNREACHABLE:     The connection establishment
  ///                           timer times out and an ICMP port unreachable error is received.
  /// EFI_ICMP_ERROR:           The connection establishment timer timeout and some other ICMP
  ///                           error is received.
  /// EFI_DEVICE_ERROR:         An unexpected system or network error occurred.
  /// EFI_NO_MEDIA:             There was a media error.
  ///
  EFI_TCP4_COMPLETION_TOKEN    CompletionToken;
} EFI_TCP4_CONNECTION_TOKEN;

typedef union {
  EFI_TCP4_CONNECTION_TOKEN    Tcp4Token;
  EFI_TCP6_CONNECTION_TOKEN    Tcp6Token;
} TCP_IO_CONNECTION_TOKEN;

注意这个结构体不涉及到具体的数据(即Packet成员)。

TCP_IO_IO_TOKEN

typedef struct {
  ///
  /// When transmission finishes or meets any unexpected error it will
  /// be set to one of the following values:
  /// EFI_SUCCESS:              The receiving or transmission operation
  ///                           completes successfully.
  /// EFI_CONNECTION_FIN:       The receiving operation fails because the communication peer
  ///                           has closed the connection and there is no more data in the
  ///                           receive buffer of the instance.
  /// EFI_CONNECTION_RESET:     The receiving or transmission operation fails
  ///                           because this connection is reset either by instance
  ///                           itself or the communication peer.
  /// EFI_ABORTED:              The receiving or transmission is aborted.
  /// EFI_TIMEOUT:              The transmission timer expires and no more
  ///                           specific information is available.
  /// EFI_NETWORK_UNREACHABLE:  The transmission fails
  ///                           because an ICMP network unreachable error is received.
  /// EFI_HOST_UNREACHABLE:     The transmission fails because an
  ///                           ICMP host unreachable error is received.
  /// EFI_PROTOCOL_UNREACHABLE: The transmission fails
  ///                           because an ICMP protocol unreachable error is received.
  /// EFI_PORT_UNREACHABLE:     The transmission fails and an
  ///                           ICMP port unreachable error is received.
  /// EFI_ICMP_ERROR:           The transmission fails and some other
  ///                           ICMP error is received.
  /// EFI_DEVICE_ERROR:         An unexpected system or network error occurs.
  /// EFI_NO_MEDIA:             There was a media error.
  ///
  EFI_TCP4_COMPLETION_TOKEN    CompletionToken;
  union {
    ///
    /// When this token is used for receiving, RxData is a pointer to EFI_TCP4_RECEIVE_DATA.
    ///
    EFI_TCP4_RECEIVE_DATA     *RxData;
    ///
    /// When this token is used for transmitting, TxData is a pointer to EFI_TCP4_TRANSMIT_DATA.
    ///
    EFI_TCP4_TRANSMIT_DATA    *TxData;
  } Packet;
} EFI_TCP4_IO_TOKEN;

typedef union {
  EFI_TCP4_IO_TOKEN    Tcp4Token;
  EFI_TCP6_IO_TOKEN    Tcp6Token;
} TCP_IO_IO_TOKEN;

TCP_IO_LISTEN_TOKEN

typedef struct {
  EFI_TCP4_COMPLETION_TOKEN    CompletionToken;
  EFI_HANDLE                   NewChildHandle;
} EFI_TCP4_LISTEN_TOKEN;

typedef union {
  EFI_TCP4_LISTEN_TOKEN    Tcp4Token;
  EFI_TCP6_LISTEN_TOKEN    Tcp6Token;
} TCP_IO_LISTEN_TOKEN;

这里的数据不再是Packet,而是一个EFI_HANDLE,它表示的是一个TCP Socket对应的虚拟Handle:

///
/// The socket structure representing a network service access point.
///
struct _TCP_SOCKET {
  EFI_HANDLE                  SockHandle;    ///< The virtual handle of the socket

TCP_IO_CLOSE_TOKEN

typedef struct {
  ///
  /// When transmission finishes or meets any unexpected error it will
  /// be set to one of the following values:
  /// EFI_SUCCESS:              The receiving or transmission operation
  ///                           completes successfully.
  /// EFI_CONNECTION_FIN:       The receiving operation fails because the communication peer
  ///                           has closed the connection and there is no more data in the
  ///                           receive buffer of the instance.
  /// EFI_CONNECTION_RESET:     The receiving or transmission operation fails
  ///                           because this connection is reset either by instance
  ///                           itself or the communication peer.
  /// EFI_ABORTED:              The receiving or transmission is aborted.
  /// EFI_TIMEOUT:              The transmission timer expires and no more
  ///                           specific information is available.
  /// EFI_NETWORK_UNREACHABLE:  The transmission fails
  ///                           because an ICMP network unreachable error is received.
  /// EFI_HOST_UNREACHABLE:     The transmission fails because an
  ///                           ICMP host unreachable error is received.
  /// EFI_PROTOCOL_UNREACHABLE: The transmission fails
  ///                           because an ICMP protocol unreachable error is received.
  /// EFI_PORT_UNREACHABLE:     The transmission fails and an
  ///                           ICMP port unreachable error is received.
  /// EFI_ICMP_ERROR:           The transmission fails and some other
  ///                           ICMP error is received.
  /// EFI_DEVICE_ERROR:         An unexpected system or network error occurs.
  /// EFI_NO_MEDIA:             There was a media error.
  ///
  EFI_TCP4_COMPLETION_TOKEN    CompletionToken;
  union {
    ///
    /// When this token is used for receiving, RxData is a pointer to EFI_TCP4_RECEIVE_DATA.
    ///
    EFI_TCP4_RECEIVE_DATA     *RxData;
    ///
    /// When this token is used for transmitting, TxData is a pointer to EFI_TCP4_TRANSMIT_DATA.
    ///
    EFI_TCP4_TRANSMIT_DATA    *TxData;
  } Packet;
} EFI_TCP4_IO_TOKEN;

typedef union {
  EFI_TCP4_CLOSE_TOKEN    Tcp4Token;
  EFI_TCP6_CLOSE_TOKEN    Tcp6Token;
} TCP_IO_CLOSE_TOKEN;

UDP_COMPLETION_TOKEN

typedef struct {
  EFI_EVENT     Event;
  EFI_STATUS    Status;
  union {
    EFI_UDP4_RECEIVE_DATA     *RxData;
    EFI_UDP4_TRANSMIT_DATA    *TxData;
  } Packet;
} EFI_UDP4_COMPLETION_TOKEN;

typedef union {
  EFI_UDP4_COMPLETION_TOKEN    Udp4;
  EFI_UDP6_COMPLETION_TOKEN    Udp6;
} UDP_COMPLETION_TOKEN;

EFI_DNS4_COMPLETION_TOKEN

///
/// EFI_DNS4_COMPLETION_TOKEN
///
typedef struct {
  ///
  /// This Event will be signaled after the Status field is updated by the EFI DNS
  /// protocol driver. The type of Event must be EFI_NOTIFY_SIGNAL.
  ///
  EFI_EVENT    Event;
  ///
  /// Will be set to one of the following values:
  ///   EFI_SUCCESS:      The host name to address translation completed successfully.
  ///   EFI_NOT_FOUND:    No matching Resource Record (RR) is found.
  ///   EFI_TIMEOUT:      No DNS server reachable, or RetryCount was exhausted without
  ///                     response from all specified DNS servers.
  ///   EFI_DEVICE_ERROR: An unexpected system or network error occurred.
  ///   EFI_NO_MEDIA:     There was a media error.
  ///
  EFI_STATUS    Status;
  ///
  /// Retry number if no response received after RetryInterval. If zero, use the
  /// parameter configured through Dns.Configure() interface.
  ///
  UINT32        RetryCount;
  ///
  /// Minimum interval of retry is 2 second. If the retry interval is less than 2
  /// seconds, then use the 2 seconds. If zero, use the parameter configured through
  /// Dns.Configure() interface.
  UINT32        RetryInterval;
  ///
  /// DNSv4 completion token data
  ///
  union {
    ///
    /// When the Token is used for host name to address translation, H2AData is a pointer
    /// to the DNS_HOST_TO_ADDR_DATA.
    ///
    DNS_HOST_TO_ADDR_DATA      *H2AData;
    ///
    /// When the Token is used for host address to host name translation, A2HData is a
    /// pointer to the DNS_ADDR_TO_HOST_DATA.
    ///
    DNS_ADDR_TO_HOST_DATA      *A2HData;
    ///
    /// When the Token is used for a general lookup function, GLookupDATA is a pointer to
    /// the DNS_GENERAL_LOOKUP_DATA.
    ///
    DNS_GENERAL_LOOKUP_DATA    *GLookupData;
  } RspData;
} EFI_DNS4_COMPLETION_TOKEN;

这个Token中的数据相比其它的Token多了很多内容,不过形式并没有太大的变化,后面还有一些类似的Token。

EFI_MTFTP4_TOKEN

struct _EFI_MTFTP4_TOKEN {
  ///
  /// The status that is returned to the caller at the end of the operation
  /// to indicate whether this operation completed successfully.
  ///
  EFI_STATUS                     Status;
  ///
  /// The event that will be signaled when the operation completes. If
  /// set to NULL, the corresponding function will wait until the read or
  /// write operation finishes. The type of Event must be
  /// EVT_NOTIFY_SIGNAL. The Task Priority Level (TPL) of
  /// Event must be lower than or equal to TPL_CALLBACK.
  ///
  EFI_EVENT                      Event;
  ///
  /// If not NULL, the data that will be used to override the existing configure data.
  ///
  EFI_MTFTP4_OVERRIDE_DATA       *OverrideData;
  ///
  /// The pointer to the null-terminated ASCII file name string.
  ///
  UINT8                          *Filename;
  ///
  /// The pointer to the null-terminated ASCII mode string. If NULL, "octet" is used.
  ///
  UINT8                          *ModeStr;
  ///
  /// Number of option/value string pairs.
  ///
  UINT32                         OptionCount;
  ///
  /// The pointer to an array of option/value string pairs. Ignored if OptionCount is zero.
  ///
  EFI_MTFTP4_OPTION              *OptionList;
  ///
  /// The size of the data buffer.
  ///
  UINT64                         BufferSize;
  ///
  /// The pointer to the data buffer. Data that is downloaded from the
  /// MTFTPv4 server is stored here. Data that is uploaded to the
  /// MTFTPv4 server is read from here. Ignored if BufferSize is zero.
  ///
  VOID                           *Buffer;
  ///
  /// The pointer to the context that will be used by CheckPacket,
  /// TimeoutCallback and PacketNeeded.
  ///
  VOID                           *Context;
  ///
  /// The pointer to the callback function to check the contents of the received packet.
  ///
  EFI_MTFTP4_CHECK_PACKET        CheckPacket;
  ///
  /// The pointer to the function to be called when a timeout occurs.
  ///
  EFI_MTFTP4_TIMEOUT_CALLBACK    TimeoutCallback;
  ///
  /// The pointer to the function to provide the needed packet contents.
  ///
  EFI_MTFTP4_PACKET_NEEDED       PacketNeeded;
};

SOCK_IO_TOKEN

typedef struct _SOCK_COMPLETION_TOKEN {
  EFI_EVENT     Event;          ///< The event to be issued
  EFI_STATUS    Status;         ///< The status to be issued
} SOCK_COMPLETION_TOKEN;

typedef struct _SOCK_IO_TOKEN {
  SOCK_COMPLETION_TOKEN    Token;
  SOCK_IO_DATA             Packet;
} SOCK_IO_TOKEN;

EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN

typedef struct {
  ///
  /// The completion status of transmitting and receiving.
  ///
  EFI_STATUS                Status;
  ///
  /// If not NULL, the event that will be signaled when the collection process
  /// completes. If NULL, this function will busy-wait until the collection process competes.
  ///
  EFI_EVENT                 CompletionEvent;
  ///
  /// The pointer to the server IP address. This address may be a unicast, multicast, or broadcast address.
  ///
  EFI_IPv4_ADDRESS          RemoteAddress;
  ///
  /// The server listening port number. If zero, the default server listening port number (67) will be used.
  ///
  UINT16                    RemotePort;
  ///
  /// The pointer to the gateway address to override the existing setting.
  ///
  EFI_IPv4_ADDRESS          GatewayAddress;
  ///
  /// The number of entries in ListenPoints. If zero, the default station address and port number 68 are used.
  ///
  UINT32                    ListenPointCount;
  ///
  /// An array of station address and port number pairs that are used as receiving filters.
  /// The first entry is also used as the source address and source port of the outgoing packet.
  ///
  EFI_DHCP4_LISTEN_POINT    *ListenPoints;
  ///
  /// The number of seconds to collect responses. Zero is invalid.
  ///
  UINT32                    TimeoutValue;
  ///
  /// The pointer to the packet to be transmitted.
  ///
  EFI_DHCP4_PACKET          *Packet;
  ///
  /// Number of received packets.
  ///
  UINT32                    ResponseCount;
  ///
  /// The pointer to the allocated list of received packets.
  ///
  EFI_DHCP4_PACKET          *ResponseList;
} EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN;

EFI_HTTP_TOKEN

///
/// EFI_HTTP_TOKEN
///
typedef struct {
  ///
  /// This Event will be signaled after the Status field is updated by the EFI HTTP
  /// Protocol driver. The type of Event must be EFI_NOTIFY_SIGNAL. The Task Priority
  /// Level (TPL) of Event must be lower than or equal to TPL_CALLBACK.
  ///
  EFI_EVENT    Event;
  ///
  /// Status will be set to one of the following value if the HTTP request is
  /// successfully sent or if an unexpected error occurs:
  ///   EFI_SUCCESS:      The HTTP request was successfully sent to the remote host.
  ///   EFI_HTTP_ERROR:   The response message was successfully received but contains a
  ///                     HTTP error. The response status code is returned in token.
  ///   EFI_ABORTED:      The HTTP request was cancelled by the caller and removed from
  ///                     the transmit queue.
  ///   EFI_TIMEOUT:      The HTTP request timed out before reaching the remote host.
  ///   EFI_DEVICE_ERROR: An unexpected system or network error occurred.
  ///
  EFI_STATUS          Status;
  ///
  /// Pointer to storage containing HTTP message data.
  ///
  EFI_HTTP_MESSAGE    *Message;
} EFI_HTTP_TOKEN;

UDP_RX_TOKEN

这里开始的Token的结构相比前面的Token已经有很大的差别:

typedef struct {
  UINT32                  Signature;
  UDP_IO                  *UdpIo;

  UDP_IO_CALLBACK         CallBack;
  VOID                    *Context;
  UINT32                  HeadLen;

  UDP_COMPLETION_TOKEN    Token;
} UDP_RX_TOKEN;

UDP_TX_TOKEN

typedef struct {
  UINT32                  Signature;
  LIST_ENTRY              Link;
  UDP_IO                  *UdpIo;
  UDP_IO_CALLBACK         CallBack;
  NET_BUF                 *Packet;
  VOID                    *Context;
  EFI_IPv4_ADDRESS        Gateway;
  UDP_SESSION_DATA        Session;
  UDP_COMPLETION_TOKEN    Token;
  UDP_TRANSMIT_DATA       Data;
} UDP_TX_TOKEN;

IP4_LINK_TX_TOKEN

typedef struct {
  UINT32                                  Signature;
  LIST_ENTRY                              Link;

  IP4_INTERFACE                           *Interface;
  IP4_SERVICE                             *IpSb;

  IP4_PROTOCOL                            *IpInstance;
  IP4_FRAME_CALLBACK                      CallBack;
  NET_BUF                                 *Packet;
  VOID                                    *Context;

  EFI_MAC_ADDRESS                         DstMac;
  EFI_MAC_ADDRESS                         SrcMac;

  EFI_MANAGED_NETWORK_COMPLETION_TOKEN    MnpToken;
  EFI_MANAGED_NETWORK_TRANSMIT_DATA       MnpTxData;
} IP4_LINK_TX_TOKEN;

IP4_LINK_RX_TOKEN

typedef struct {
  UINT32                                  Signature;
  IP4_INTERFACE                           *Interface;

  IP4_PROTOCOL                            *IpInstance;
  IP4_FRAME_CALLBACK                      CallBack;
  VOID                                    *Context;

  EFI_MANAGED_NETWORK_COMPLETION_TOKEN    MnpToken;
} IP4_LINK_RX_TOKEN;

SOCK_TOKEN

typedef struct _SOCK_TOKEN {
  LIST_ENTRY               TokenList;     ///< The entry to add in the token list
  SOCK_COMPLETION_TOKEN    *Token;        ///< The application's token
  UINT32                   RemainDataLen; ///< Unprocessed data length
  SOCKET                   *Sock;         ///< The pointer to the socket this token
                                          ///< belongs to
} SOCK_TOKEN;

IpIoLib

UDP4和TCP4中,有不少操作实际上是放在IpIoLib库中完成的,它实际上是上层网络驱动对IP层调用的包装接口。

IpIoLib首先对IPv4和IPv6中会使用到的数据进行了包装,这样就可以用统一的接口来处理IP层,比如:

typedef union {
  EFI_IP4_COMPLETION_TOKEN    Ip4Token;
  EFI_IP6_COMPLETION_TOKEN    Ip6Token;
} IP_IO_IP_COMPLETION_TOKEN;

typedef union {
  EFI_IP4_TRANSMIT_DATA    Ip4TxData;
  EFI_IP6_TRANSMIT_DATA    Ip6TxData;
} IP_IO_IP_TX_DATA;

typedef union {
  EFI_IP4_RECEIVE_DATA    Ip4RxData;
  EFI_IP6_RECEIVE_DATA    Ip6RxData;
} IP_IO_IP_RX_DATA;

typedef union {
  EFI_IP4_OVERRIDE_DATA    Ip4OverrideData;
  EFI_IP6_OVERRIDE_DATA    Ip6OverrideData;
} IP_IO_OVERRIDE;

typedef union {
  EFI_IP4_CONFIG_DATA    Ip4CfgData;
  EFI_IP6_CONFIG_DATA    Ip6CfgData;
} IP_IO_IP_CONFIG_DATA;

typedef union {
  EFI_IP4_HEADER    *Ip4Hdr;
  EFI_IP6_HEADER    *Ip6Hdr;
} IP_IO_IP_HEADER;

typedef union {
  EFI_IP4_PROTOCOL    *Ip4;
  EFI_IP6_PROTOCOL    *Ip6;
} IP_IO_IP_PROTOCOL;

这里包含了收发和处理数据的结构体,配置IP的结构体等内容,另外还有一个重要的结构体IP_IO,会在后续进一步介绍。

IP_IO

IP_IO是对IP4和IP6接口的包装,这样上层的TCP和UDP就可以直接使用它而不需要分别对待IP4和IP6。该结构体位于NetworkPkg\Include\Library\IpIoLib.h:

///
/// This data structure wraps Ip4/Ip6 instances. The IpIo Library uses it for all
/// Ip4/Ip6 operations.
///
typedef struct _IP_IO {
  ///
  /// The node used to link this IpIo to the active IpIo list.
  ///
  LIST_ENTRY                   Entry;

  ///
  /// The list used to maintain the IP instance for different sending purpose.
  ///
  LIST_ENTRY                   IpList;

  EFI_HANDLE                   Controller;
  EFI_HANDLE                   Image;
  EFI_HANDLE                   ChildHandle;
  //
  // The IP instance consumed by this IP_IO
  //
  IP_IO_IP_PROTOCOL            Ip;
  BOOLEAN                      IsConfigured;

  ///
  /// Some ip configuration data can be changed.
  ///
  UINT8                        Protocol;

  ///
  /// Token and event used to get data from IP.
  ///
  IP_IO_IP_COMPLETION_TOKEN    RcvToken;

  ///
  /// List entry used to link the token passed to IP_IO.
  ///
  LIST_ENTRY                   PendingSndList;

  //
  // User interface used to get notify from IP_IO
  //
  VOID                         *RcvdContext;     ///< See IP_IO_OPEN_DATA::RcvdContext.
  VOID                         *SndContext;      ///< See IP_IO_OPEN_DATA::SndContext.
  PKT_RCVD_NOTIFY              PktRcvdNotify;    ///< See IP_IO_OPEN_DATA::PktRcvdNotify.
  PKT_SENT_NOTIFY              PktSentNotify;    ///< See IP_IO_OPEN_DATA::PktSentNotify.
  UINT8                        IpVersion;
  IP4_ADDR                     StationIp;
  IP4_ADDR                     SubnetMask;
} IP_IO;

该结构体通过IpIoCreate()函数创建,它们在UDP和TCP模块中使用,比如TCP中:

EFI_STATUS
TcpCreateService (
  IN EFI_HANDLE  Controller,
  IN EFI_HANDLE  Image,
  IN UINT8       IpVersion
  )
{
  TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion);

还有UDP中:

EFI_STATUS
Udp4CreateService (
  IN OUT UDP4_SERVICE_DATA  *Udp4Service,
  IN     EFI_HANDLE         ImageHandle,
  IN     EFI_HANDLE         ControllerHandle
  )
{
  Udp4Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_4);

下面介绍IP_IO中比较重要的成员:

  • IpList:IP实例链表,它在IpIoCreate()中初始化,并在IpIoAddIp()中添加链表成员:
IP_IO_IP_INFO *
EFIAPI
IpIoAddIp (
  IN OUT IP_IO  *IpIo
  )
{
  InsertTailList (&IpIo->IpList, &IpInfo->Entry);

这里涉及到另外一个结构体IP_IO_IP_INFO

typedef struct _IP_IO_IP_INFO {
  EFI_IP_ADDRESS               Addr;
  IP_IO_IP_MASK                PreMask;
  LIST_ENTRY                   Entry;
  EFI_HANDLE                   ChildHandle;
  IP_IO_IP_PROTOCOL            Ip;
  IP_IO_IP_COMPLETION_TOKEN    DummyRcvToken;
  INTN                         RefCnt;
  UINT8                        IpVersion;
} IP_IO_IP_INFO;

这个结构体也是在IpIoAddIp()中创建的,这个函数虽然说是增加IP_IO_IP_INFO,但是其中只是对IP_IO_IP_INFO做了初始化而已,其中最重要的代码是:

IP_IO_IP_INFO *
EFIAPI
IpIoAddIp (
  IN OUT IP_IO  *IpIo
  )
{
  Status = IpIoCreateIpChildOpenProtocol (
             IpIo->Controller,
             IpIo->Image,
             &IpInfo->ChildHandle,
             IpInfo->IpVersion,
             (VOID **)&IpInfo->Ip
             );
  // 中间略
  InsertTailList (&IpIo->IpList, &IpInfo->Entry);

IpIoCreateIpChildOpenProtocol()打开了gEfiIp4ProtocolGuid对应的EFI_IP4_PROTOCOL(当然实际代码中是IP_IO_IP_PROTOCOL)。所以IP_IO_IP_INFO这个结构体的重点是对IP通信接口EFI_IP4_PROTOCOL的包装。最终IpIoAddIp()函数会被上层的TCP和UDP使用,来创建它们的IP通信接口:

// TCP模块中:
EFI_STATUS
TcpAttachPcb (
  IN SOCKET  *Sk
  )
{
  Tcb->IpInfo = IpIoAddIp (IpIo);

// UDP模块中:
EFI_STATUS
EFIAPI
Udp4ServiceBindingCreateChild (
  IN EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                    *ChildHandle
  )
{
  Instance->IpInfo = IpIoAddIp (Udp4Service->IpIo);

通过上述的方式,UPD和TCP就与其下层的IP联系到了一起:

在这里插入图片描述

  • Ip:对IP4和IP6的Protocol的包装,我们主要关注IP4就可以了:
typedef union {
  EFI_IP4_PROTOCOL    *Ip4;
  EFI_IP6_PROTOCOL    *Ip6;
} IP_IO_IP_PROTOCOL;
  • RcvToken:如注释描述,是从IP4获取数据的Token。
  • PendingSndList:需要传递给IP4的Token的列表。
  • RcvdContextSndContextPktRcvdNotifyPktSentNotify:对应数据处理的函数,它们来自另外的一个结构体IP_IO_OPEN_DATA
///
/// The struct is for the user to pass IP configuration and callbacks to IP_IO.
/// It is used by IpIoOpen().
///
typedef struct _IP_IO_OPEN_DATA {
  IP_IO_IP_CONFIG_DATA    IpConfigData;  ///< Configuration of the IP instance.
  VOID                    *RcvdContext;  ///< Context data used by receive callback.
  VOID                    *SndContext;   ///< Context data used by send callback.
  PKT_RCVD_NOTIFY         PktRcvdNotify; ///< Receive callback.
  PKT_SENT_NOTIFY         PktSentNotify; ///< Send callback.
} IP_IO_OPEN_DATA;

IP_IO_OPEN_DATA其实是一个临时参数,会在打开IP_IO的时候使用,比如UDP模块中:

EFI_STATUS
Udp4CreateService (
  IN OUT UDP4_SERVICE_DATA  *Udp4Service,
  IN     EFI_HANDLE         ImageHandle,
  IN     EFI_HANDLE         ControllerHandle
  )
{
  OpenData.RcvdContext           = (VOID *)Udp4Service;
  OpenData.SndContext            = NULL;
  OpenData.PktRcvdNotify         = Udp4DgramRcvd;
  OpenData.PktSentNotify         = Udp4DgramSent;
  Status = IpIoOpen (Udp4Service->IpIo, &OpenData);

还有TCP中:

EFI_STATUS
TcpCreateService (
  IN EFI_HANDLE  Controller,
  IN EFI_HANDLE  Image,
  IN UINT8       IpVersion
  )
{
  ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));

  if (IpVersion == IP_VERSION_4) {
    CopyMem (
      &OpenData.IpConfigData.Ip4CfgData,
      &mIp4IoDefaultIpConfigData,
      sizeof (EFI_IP4_CONFIG_DATA)
      );
    OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
  } else {
    CopyMem (
      &OpenData.IpConfigData.Ip6CfgData,
      &mIp6IoDefaultIpConfigData,
      sizeof (EFI_IP6_CONFIG_DATA)
      );
    OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
  }

  OpenData.PktRcvdNotify = TcpRxCallback;
  Status                 = IpIoOpen (TcpServiceData->IpIo, &OpenData);

都是在创建TCP或者UDP服务时作为参数使用。这个数据中最重要的是两个回调函数,分别对应IP数据收发的处理:

/**
  The prototype is called back when an IP packet is received.

  @param[in] Status        The result of the receive request.
  @param[in] IcmpErr       Valid when Status is EFI_ICMP_ERROR.
  @param[in] NetSession    The IP session for the received packet.
  @param[in] Pkt           The packet received.
  @param[in] Context       The data provided by the user for the received packet when
                           the callback is registered in IP_IO_OPEN_DATA::RcvdContext.

**/
typedef
VOID
(EFIAPI *PKT_RCVD_NOTIFY)(
  IN EFI_STATUS           Status,
  IN UINT8                IcmpErr,
  IN EFI_NET_SESSION_DATA *NetSession,
  IN NET_BUF              *Pkt,
  IN VOID                 *Context
  );

/**
  The prototype is called back when an IP packet is sent.

  @param[in] Status        Result of the IP packet being sent.
  @param[in] Context       The data provided by user for the received packet when
                           the callback is registered in IP_IO_OPEN_DATA::SndContext.
  @param[in] Sender        A Union type to specify a pointer of EFI_IP4_PROTOCOL
                           or EFI_IP6_PROTOCOL.
  @param[in] NotifyData    The Context data specified when calling IpIoSend()

**/
typedef
VOID
(EFIAPI *PKT_SENT_NOTIFY)(
  IN EFI_STATUS        Status,
  IN VOID              *Context,
  IN IP_IO_IP_PROTOCOL Sender,
  IN VOID              *NotifyData
  );
  • IpVersion:对应IP_VERSION_4或者IP_VERSION_6,值分别是4和6,表示IPv4和IPv6。
  • StationIpSubnetMask:IP使用的地址和掩码。

函数

比较重要的函数:

/**
  Create a new IP_IO instance.

  If IpVersion is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().

  This function uses IP4/IP6 service binding protocol in Controller to create
  an IP4/IP6 child (aka IP4/IP6 instance).

  @param[in]  Image             The image handle of the driver or application that
                                consumes IP_IO.
  @param[in]  Controller        The controller handle that has IP4 or IP6 service
                                binding protocol installed.
  @param[in]  IpVersion         The version of the IP protocol to use, either
                                IPv4 or IPv6.

  @return The pointer to a newly created IP_IO instance, or NULL if failed.

**/
IP_IO *
EFIAPI
IpIoCreate (
  IN EFI_HANDLE  Image,
  IN EFI_HANDLE  Controller,
  IN UINT8       IpVersion
  );

/**
  Open an IP_IO instance for use.

  If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().

  This function is called after IpIoCreate(). It is used for configuring the IP
  instance and register the callbacks and their context data for sending and
  receiving IP packets.

  @param[in, out]  IpIo               The pointer to an IP_IO instance that needs
                                      to open.
  @param[in]       OpenData           The configuration data and callbacks for
                                      the IP_IO instance.

  @retval          EFI_SUCCESS            The IP_IO instance opened with OpenData
                                          successfully.
  @retval          EFI_ACCESS_DENIED      The IP_IO instance is configured, avoid to
                                          reopen it.
  @retval          EFI_UNSUPPORTED        IPv4 RawData mode is no supported.
  @retval          EFI_INVALID_PARAMETER  Invalid input parameter.
  @retval          Others                 Error condition occurred.

**/
EFI_STATUS
EFIAPI
IpIoOpen (
  IN OUT IP_IO            *IpIo,
  IN     IP_IO_OPEN_DATA  *OpenData
  );

/**
  Send out an IP packet.

  This function is called after IpIoOpen(). The data to be sent is wrapped in
  Pkt. The IP instance wrapped in IpIo is used for sending by default but can be
  overridden by Sender. Other sending configs, like source address and gateway
  address etc., are specified in OverrideData.

  @param[in, out]  IpIo                  Pointer to an IP_IO instance used for sending IP
                                         packet.
  @param[in, out]  Pkt                   Pointer to the IP packet to be sent.
  @param[in]       Sender                The IP protocol instance used for sending.
  @param[in]       Context               Optional context data.
  @param[in]       NotifyData            Optional notify data.
  @param[in]       Dest                  The destination IP address to send this packet to.
                                         This parameter is optional when using IPv6.
  @param[in]       OverrideData          The data to override some configuration of the IP
                                         instance used for sending.

  @retval          EFI_SUCCESS           The operation is completed successfully.
  @retval          EFI_INVALID_PARAMETER The input parameter is not correct.
  @retval          EFI_NOT_STARTED       The IpIo is not configured.
  @retval          EFI_OUT_OF_RESOURCES  Failed due to resource limit.
  @retval          Others                Error condition occurred.

**/
EFI_STATUS
EFIAPI
IpIoSend (
  IN OUT IP_IO           *IpIo,
  IN OUT NET_BUF         *Pkt,
  IN     IP_IO_IP_INFO   *Sender        OPTIONAL,
  IN     VOID            *Context       OPTIONAL,
  IN     VOID            *NotifyData    OPTIONAL,
  IN     EFI_IP_ADDRESS  *Dest          OPTIONAL,
  IN     IP_IO_OVERRIDE  *OverrideData  OPTIONAL
  );

/**
  Add a new IP instance for sending data.

  If IpIo is NULL, then ASSERT().
  If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().

  The function is used to add the IP_IO to the IP_IO sending list. The caller
  can later use IpIoFindSender() to get the IP_IO and call IpIoSend() to send
  data.

  @param[in, out]  IpIo               The pointer to an IP_IO instance to add a new IP
                                      instance for sending purposes.

  @return The pointer to the created IP_IO_IP_INFO structure; NULL if failed.

**/
IP_IO_IP_INFO *
EFIAPI
IpIoAddIp (
  IN OUT IP_IO  *IpIo
  );

/**
  Configure the IP instance of this IpInfo and start the receiving if IpConfigData
  is not NULL.

  If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().

  @param[in, out]  IpInfo          The pointer to the IP_IO_IP_INFO instance.
  @param[in, out]  IpConfigData    The IP4 or IP6 configure data used to configure
                                   the IP instance. If NULL, the IP instance is reset.
                                   If UseDefaultAddress is set to TRUE, and the configure
                                   operation succeeds, the default address information
                                   is written back in this IpConfigData.

  @retval          EFI_SUCCESS     The IP instance of this IpInfo was configured successfully,
                                   or there is no need to reconfigure it.
  @retval          Others          The configuration failed.

**/
EFI_STATUS
EFIAPI
IpIoConfigIp (
  IN OUT IP_IO_IP_INFO  *IpInfo,
  IN OUT VOID           *IpConfigData OPTIONAL
  );

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/290991.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java IO流介绍以及缓冲为何能提升性能

概念&#xff1a; 流是一种抽象概念&#xff0c;它代表了数据的无结构化传递。按照流的方式进行输入输出&#xff0c;数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作&#xff0c;而向流中添加数据的操作称为插入操作。 Java IO 也称为IO流&#xff0c;…

中文大语言模型 Llama-2 7B(或13B) 本地化部署 (国内云服务器、GPU单卡16GB、中文模型、WEB页面TextUI、简单入门)

本文目的是让大家先熟悉模型的部署&#xff0c;简单入门&#xff1b;所以只需要很小的算力&#xff0c;单台服务器 单GPU显卡&#xff08;显存不低于12GB&#xff09;&#xff0c;操作系统需要安装 Ubuntu 18.04。 1 服务器&操作系统 1.1服务器的准备 准备一台服务器 单张…

【论文阅读笔记】两篇完整模态脑瘤分割

两篇完整模态脑瘤分割论文&#xff0c;都是使用Transformer&#xff0c;没有什么特别的特色&#xff0c;也没有开源代码&#xff0c;因此只是简单记录一下。 3D CATBraTS: Channel attention transformer for brain tumour semantic segmentation El Badaoui R, Coll E B, Ps…

Linux第2步_创建虚拟机

VMware软件安装好后&#xff0c;就可以创建虚拟机了。 一、虚拟机对CPU的要求较高 i7 处理器&#xff1a;CPU&#xff1a;Intel(R) Core(TM) i7-8700 CPU 3.20GHz 3.19 GHz 内核数&#xff1a;6 线程数&#xff1a; 12 最大睿频频率&#xff1a; 4.60 GHz 英特尔 睿…

springcloud之集成nacos config

写在前面 源码 。 本文看下如下集成nacos config组件。 1&#xff1a;常见配置方式分析 我们先来看下常见的配置方式都有哪些&#xff0c;以及其有什么优点和缺点。 硬编码 优点&#xff1a;hardcode&#xff0c;除了开发的时候快些&#xff0c;爽一下&#xff0c;有个屁优…

网络机顶盒哪个好?耗时30天盘点网络机顶盒排名

网络机顶盒作为电视机的最佳搭档&#xff0c;是看片必备&#xff0c;网络机顶盒的品牌非常多让新手们在选购时往往不知道网络机顶盒哪个好&#xff0c;我耗时一个月测评了十几款热门的电视机顶盒&#xff0c;通过各个角度深度对比后整理了网络机顶盒排名&#xff0c;在选购时大…

NFC物联网开发在智慧校园中的应用

近年来&#xff0c;校园信息化建设速度加快&#xff0c;以物联网为基础、以各种应用服务系统为载体的智慧校园将教学、管理和校园生活充分融合&#xff0c;形成了工作、学习和生活的一体化环境。沉寂已久的NEC 技术&#xff0c;得益于智能手机的普及、无线网络数据速率提高&…

如何构建高效测试体系?掌握5大自动化测试模式就够了

软件开发过程中&#xff0c;高效的自动化测试体系是提升测试效率、保证产品质量关键&#xff0c;一个全面的测试体系涵盖多个维度&#xff0c;从功能性到用户界面&#xff0c;再到性能和安全性。 每个维度均采用不同的测试模式来满足特定的需求和解决特别的挑战&#xff0c;本…

Linux_源码编译安装LAMP

1. 安装httpd服务 在配置 Apache 网站服务之前&#xff0c;需要正确安装好 httpd 服务器软件。httpd 服务器的安装可以选用 RPM 安装、源码编译安装这两种方式&#xff0c;前者相对比较简单、快速&#xff0c;但是在功能上存在一定的局限性。在实际的生产环境中&#xff0c;使…

openGauss学习笔记-174 openGauss 数据库运维-备份与恢复-导入数据-管理并发写入操作

文章目录 openGauss学习笔记-174 openGauss 数据库运维-备份与恢复-导入数据-管理并发写入操作174.1 事务隔离说明174.2 写入和读写操作174.3 并发写入事务的潜在死锁情况 openGauss学习笔记-174 openGauss 数据库运维-备份与恢复-导入数据-管理并发写入操作 174.1 事务隔离说…

K线+直线 现货黄金也可能变现

现货黄金行情怎么做&#xff0c;这是投资者需要思考的问题。幸运的是&#xff0c;现在市面上有很多书籍&#xff0c;是其他有经验、有想法的投资者们对其经验的总结和分享&#xff0c;此外网络上还有不同的文章和各种各样的视频介绍相关交易经验&#xff0c;这都是可以让我们借…

VUE3跳转页面时 定时器未清除解决

一,问题 1、在vue中使用setTimeout定时器的时候&#xff0c;可能会遇到关不掉的情况&#xff0c;会存在明明已经在beforeDestroy和destroyed中设置了定时器清除了&#xff0c;但是有时候没生效&#xff0c;定时器还会继续执行。 2、在这里需要说一下setTimeout的使用场景&…

Kubernetes 配置Pod使用代理上网

配置Kubernetes Pod使用代理上网 在企业网络环境中进行Kubernetes集群的管理时&#xff0c;经常会遇到需要配置Pods通过HTTP代理服务器访问Internet的情况。这可能是由于各种原因&#xff0c;如安全策略限制、网络架构要求或者访问特定资源的需要。本文将介绍配置Kubernetes中…

天融信TOPSEC Cookie 远程命令执行漏洞复现

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 一、漏洞概述 天融信TOPSEC解决方案包括综合管理系统&#xff0c;各类安…

科研上新 | 第4期:语言-音乐对比预训练;查找表实现的神经网络推理;大模型时代重新定义搜索框架

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 本期内容速览 …

草图大师 sketchup pro2023

SketchUp Pro是一款功能强大的三维建模软件&#xff0c;适用于建筑、机械、室内设计等领域。它提供了丰富的绘图工具和灵活的建模选项&#xff0c;支持实时预览和多种设备适配&#xff0c;让用户能够快速高效地创建出逼真的三维模型。SketchUp Pro还具备强大的插件生态和团队协…

【mars3d】FixedRoute的circle没有跟polyline贴着模型的解决方案

问题&#xff1a;【mars3d】官网的贴模型示例中&#xff0c;参考api文档增加了circle的配置&#xff0c;但是FixedRoute的circle没有跟polyline贴着模型 circle: { radius: 10, materialType: mars3d.MaterialType.CircleWave, materialOptions: { color: "#ffff00"…

数仓分层结构

--图片来源尚硅谷 ODS层&#xff1a; 数据存储格式&#xff1a;JSON/TSV gzip压缩&#xff08;默认&#xff09; Operate Data Store -- 存储从mysql业务数据库和日志服务器的日志文件中采集到的数据 -- 日志数据 -- 格式:JSON --业务数据 --历史数据 …

借还款记账表,借款还款记账软件

我们每个人都在为生活奔波&#xff0c;为事业打拼。但有时候&#xff0c;生活中的一些小事情&#xff0c;比如朋友间的借贷、还款&#xff0c;就可能让我们的生活变得有些混乱。为了解决这个问题&#xff0c;一个全新的借还款记账软件【晨曦记账本】横空出世&#xff0c;它不仅…

STM32真的是很落后吗?

今日话题&#xff0c;STM32真的是很落后吗&#xff1f;STM32是否落后于其他技术在很大程度上依赖于具体的应用和需求。虽然我对Python的了解有限&#xff0c;但我认识到STM32在嵌入式领域具有广泛的应用和卓越的性能。值得注意的是&#xff0c;STM32不仅性能卓越&#xff0c;而…