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

news2024/11/27 10:37:38

通用函数和数据

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
  );

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

知识付费平台搭建?找明理信息科技,专业且高效

明理信息科技知识付费saas租户平台 在当今数字化时代&#xff0c;知识付费已经成为一种趋势&#xff0c;越来越多的人愿意为有价值的知识付费。然而&#xff0c;公共知识付费平台虽然内容丰富&#xff0c;但难以满足个人或企业个性化的需求和品牌打造。同时&#xff0c;开发和…

【MATLAB第88期】基于MATLAB的6种神经网络(ANN、FFNN、CFNN、RNN、GRNN、PNN)多分类预测模型对比含交叉验证

【MATLAB第88期】基于MATLAB的6种神经网络&#xff08;ANN、FFNN、CFNN、RNN、GRNN、PNN&#xff09;多分类预测模型对比含交叉验证 前言 本文介绍六种类型的神经网络分类预测模型 1.模型选择 前馈神经网络 (FFNN) 人工神经网络 (ANN) 级联前向神经网络 (CFNN) 循环神经网…

QT上位机开发(串口界面设计)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 如果上位机要和嵌入式设备进行打交道的话&#xff0c;那么串口可能就是我们遇到的第一个硬件设备。串口的物理接线很简单&#xff0c;基本上就是收…

前端-relation-graph实现关系数据展示(关系图/流程图)

目录 前言&#xff1a; 1. relation-graph 2. relation-graph数据关系组件---官方地址relation-graph - A Relationship Graph Componenthttps://www.relation-graph.com/ 3. 选择relation-graph的理由 4. 项目中引用relation-graph 4.1 下载命令 4.2 在Vue 2 中使用 4…

CGAL的无限制的Delaunay图

本章描述了构建L∞距离下线段Delaunay图的算法和几何特征。这些特征还包括绘制L∞距离下线段Delaunay图对偶&#xff08;即L∞距离下线段Voronoi图&#xff09;边缘的方法。L∞算法和特征依赖于欧几里得&#xff08;或L2&#xff09;距离下的线段Delaunay图算法和特征。L∞度量…

【动态规划】C++算法:44 通配符匹配

作者推荐 【动态规划】【字符串】扰乱字符串 本文涉及的基础知识点 动态规划 LeetCode44 通配符匹配 给你一个输入字符串 (s) 和一个字符模式 &#xff0c;请你实现一个支持 ‘?’ 和 ‘’ 匹配规则的通配符匹配&#xff1a; ‘?’ 可以匹配任何单个字符。 ’ 可以匹配…

动手学深度学习之卷积神经网络之池化层

池化层 卷积层对位置太敏感了&#xff0c;可能一点点变化就会导致输出的变化&#xff0c;这时候就需要池化层了&#xff0c;池化层的主要作用就是缓解卷积层对位置的敏感性 二维最大池化 这里有一个窗口&#xff0c;来滑动&#xff0c;每次我们将窗口中最大的值给拿出来 还是上…

大创项目推荐 深度学习人脸表情识别算法 - opencv python 机器视觉

文章目录 0 前言1 技术介绍1.1 技术概括1.2 目前表情识别实现技术 2 实现效果3 深度学习表情识别实现过程3.1 网络架构3.2 数据3.3 实现流程3.4 部分实现代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习人脸表情识别系…

【JavaFX】JavaFX11开发踩坑记录

文章目录 技术栈踩坑记录 技术栈 JavaFX 11MavenJDK 11 踩坑记录 这些坑对于初学者很容易踩&#xff0c;JavaFX经常会报错空指针异常遇到其中一个问题可能就会消耗好几天的时间。 JavaFX 采用的是MVC架构设计&#xff0c;页面设计使用 fxml文件&#xff1b;业务逻辑采用Con…

k8s的网络

k8s的网络 k8s中的通信模式&#xff1a; 1、pod内部之间容器与容器之间的通信 在同一个pod中的容器共享资源和网络&#xff0c;使用同一个网络命名空间&#xff0c;可以直接通信的 2、同一个node节点之内&#xff0c;不同pod之间的通信 每个pod都有一个全局的真实的ip地址…

qt 异常汇总

1. C2338 No Q_OBJECT in the class with the signal (编译源文件 ..\..\qt\labelme-master\src\mainwindow.cpp mainwindow头文件中的类没有Q_OBJECT宏定义&#xff0c;或者其子类或者其他依赖没有Q_OBJECT宏定义。 全部qt类都要写上Q_OBJECT. 2. C2385 对connect的访…

AI:116-基于深度学习的视频行为识别与分析

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

ctfshow——PHP特性

文章目录 web 89web 90web 91web 92web 93web 94web 95web 96web 97web 98web 99 web 89 使用人工分配 ID 键的数值型数组绕过preg_match. 两个函数&#xff1a; preg_match()&#xff1a;执行正则表达式&#xff0c;进行字符串过滤。preg_match函数用法&#xff0c;正则表达式…

uni-app 前后端调用实例 基于Springboot 详情页实现

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

【Java EE初阶七】多线程案例(生产者消费者模型)

1. 阻塞队列 队列是先进先出的一种数据结构&#xff1b; 阻塞队列&#xff0c;是基于队列&#xff0c;做了一些扩展&#xff0c;适用于多线程编程中&#xff1b; 阻塞队列特点如下&#xff1a; 1、是线程安全的 2、具有阻塞的特性 2.1、当队列满了时&#xff0c;就不能往队列里…

Yapi安装配置(CentOs)

环境要求 nodejs&#xff08;7.6) mongodb&#xff08;2.6&#xff09; git 准备工作 清除yum命令缓存 sudo yum clean all卸载低版本nodejs yum remove nodejs npm -y安装nodejs,获取资源,安装高版本nodejs curl -sL https://rpm.nodesource.com/setup_8.x | bash - #安装 s…

图片中src属性绑定不同的路径

vue3 需求是按钮disable的时候&#xff0c;显示灰色的icon&#xff1b;非disable状态&#xff0c;显示白色的icon 一开始src写成三元表达式&#xff0c;发现不行&#xff0c;网上说src不能写成三元表达式&#xff0c;vue会识别成字符串 最后的解决方案 同时&#xff0c;发现…

win下持续观察nvidia-smi

简介&#xff1a;在Windows操作系统中&#xff0c;没有与Linux中watch命令直接对应的内置工具&#xff0c;但有1种方法快速简单的方法可以实现类似的效果&#xff0c;尤其是用于监控类似于nvidia-smi的命令输出。 历史攻略&#xff1a; Python&#xff1a;查看windows下GPU的…

美国地质调查局历史地形图

简介 美国地质调查局地形图的历史可以追溯到 19 世纪末&#xff0c;当时美国地质调查局开始着手绘制整个美国的详细地图。1:24,000 比例尺&#xff0c;也称为 7.5 分四边形地图&#xff0c;成为最广泛使用的比例尺之一。每张地图覆盖 7.5 分经纬度的区域&#xff0c;从而详细呈…

在Docker中安装Tomact

目录 前言&#xff1a; 一.安装Tomact 查找指定的tomact版本 下载tomact9.0 查看该镜像是否安装成功 安装成功之后就开始运行镜像了 ps&#xff08;用于列出正在运行的Docker容器&#xff09; ​编辑 测试(虚拟机ip:8080) ​编辑 解决措施 ​编辑 完成以上步骤&…