【UEFI基础】UEFI网络框架之MNP

MNP

MNP实际上包含两个协议:

1)Manager Network Service Binding Protocol

2)Manager Network Protocol

MNP跟SNP一样,也只是纯数据包的传输,但是它与SNP最大的不同在于,MNP可以处理VLAN。

并且为了处理VLAN,它需要使用到UEFI中另一种Protocol:Service Binding Protocol,形如下面代码:

EFI_SERVICE_BINDING_PROTOCOL    mMnpServiceBindingProtocol = {
  MnpServiceBindingCreateChild,
  MnpServiceBindingDestroyChild
};

前面已经提到了一种叫做Driver Binding Protocol的结构。而这里又引入了Service Binding Protocol的形式(对应到MNP的是gEfiManagedNetworkServiceBindingProtocolGuid这个GUID),主要就是为了处理VLAN。

对于SNP,它只需要处理UNDI/NII传上来的数据即可,即一个协议对应一个网卡。

但是对于MNP来说,由于VLAN的出现,MNP需要对数据进行区分处理,这时实际上MNP需要处理的是一种树形的结构,MNP在根部。

而对于Driver Bingding Protocol,通过它安装的Protocol,只能是单一的线性结构的,即一个handle上安装一个MNP。但是显然对于多个VLAN的情况下,这是不合适的。

为了解决这个问题,所以引入了Service  Binding Protocol。它的作用是,通过创建子handle来对应同一张网卡下传输过来的不同的VLAN数据。这也是上述代码中的CreateChild的含义。

因此不同于SNP一个网卡对应一个SNP_DRIVER结构体,MNP需要有三个结构体。

首先说明MNP的初始化过程中的几个最主要的动作:

1. 通过MnpInitializeDeviceData()函数初始化MNP_DEVICE_DATA结构体;

2. 通过MnpCreateServiceData()函数初始化MNP_SERVICE_DATA结构体;

3. 安装gEfiVlanConfigProtocolGuid对应的Protocol;

4. 安装gEfiManagedNetworkServiceBindingProtocolGuid对应的Protocol;

需要特别注意的,MNP的初始化过程中并没有安装gEfiManagedNetworkProtocolGuid对应的Protocol,这与SNP不同

MNP_DEVICE_DATA结构体是一个网卡对应一个,而MNP_SERVICE_DATA是每种不同的VLAN对应一个。

在MNP_DEVICE_DATA结构体中有一个成员ServiceList,它指向了该网卡下所有VLAN对应的MNP_SERVICE_DATA,拓扑结构如下图所示:

为了更好的了解MNP,最重要的就是了解这两个结构体。

MNP_SERVICE_DATA structure

MNP_SERVICE_DATA定义在MnpDriver.h中,如下所示:

typedef struct {
  UINT32                        Signature;

  LIST_ENTRY                    Link;

  MNP_DEVICE_DATA               *MnpDeviceData;
  EFI_HANDLE                    ServiceHandle;
  EFI_SERVICE_BINDING_PROTOCOL  ServiceBinding;
  EFI_DEVICE_PATH_PROTOCOL      *DevicePath;

  LIST_ENTRY                    ChildrenList;
  UINTN                         ChildrenNumber;

  UINT32                        Mtu;

  UINT16                        VlanId;
  UINT8                         Priority;
} MNP_SERVICE_DATA;

这些值的介绍如下:

1. Signature:它的值是SIGNATURE_32 ('M', 'n', 'p', 'S'),反正就是一个标识,没有什么好介绍的;

2. Link:它与对应的MNP_DEVICE_DATA的ServiceList连接;

3. MnpDeviceData:就是对应网卡的MNP_DEVICE_DATA;

4. ServiceHandleServiceBinding:gEfiManagedNetworkServiceBindingProtocolGuid对应的Protocol和Handle,每一个MNP_SERVICE_DATA结构都对应一个,是在MnpCreateServiceData()函数中安装的;

5. DevicePath:它的值就是在原来网卡的DevicePath上再添加VLAN的部分,具体可以参考MnpCreateVlanChild()函数;

6. ChildrenListChildrenNumber:这两个值涉及到EFI_SERVICE_BINDING_PROTOCOL的CreateChild()函数创建的实例,这里就是MNP_INSTANCE_DATA结构体(第三个重要的结构体),通过这两个成员可以访问到MNP_INSTANCE_DATA结构体,它包含了EFI_MANAGED_NETWORK_PROTOCOL成员,这才是收发数据的主体;

7. Mtu:最大传输单元会因为Vlan的引入而减少几个字节(一般是4个字节);

8. VlanIdPriority:Vlan的值;

MNP_DEVICE_DATA structure

MNP_DEVICE_DATA定义在MnpDriver.h中,如下所示:

typedef struct {
  UINT32                        Signature;

  EFI_HANDLE                    ControllerHandle;
  EFI_HANDLE                    ImageHandle;

  EFI_VLAN_CONFIG_PROTOCOL      VlanConfig;
  UINTN                         NumberOfVlan;
  CHAR16                        *MacString;
  EFI_SIMPLE_NETWORK_PROTOCOL   *Snp;

  //
  // List of MNP_SERVICE_DATA
  //
  LIST_ENTRY                    ServiceList;
  //
  // Number of configured MNP Service Binding child
  //
  UINTN                         ConfiguredChildrenNumber;

  LIST_ENTRY                    GroupAddressList;
  UINT32                        GroupAddressCount;

  LIST_ENTRY                    FreeTxBufList;
  LIST_ENTRY                    AllTxBufList;
  UINT32                        TxBufCount;

  NET_BUF_QUEUE                 FreeNbufQue;
  INTN                          NbufCnt;

  EFI_EVENT                     PollTimer;
  BOOLEAN                       EnableSystemPoll;

  EFI_EVENT                     TimeoutCheckTimer;
  EFI_EVENT                     MediaDetectTimer;

  UINT32                        UnicastCount;
  UINT32                        BroadcastCount;
  UINT32                        MulticastCount;
  UINT32                        PromiscuousCount;

  //
  // The size of the data buffer in the MNP_PACKET_BUFFER used to
  // store a packet.
  //
  UINT32                        BufferLength;
  UINT32                        PaddingSize;
  NET_BUF                       *RxNbufCache;
} MNP_DEVICE_DATA;

这些值的介绍如下:

1. Signature:值是SIGNATURE_32 ('M', 'n', 'p', 'D');

2. ControllerHandleImageHandle:对应到EFI_DRIVER_BINDING_PROTOCOL中的两个Handle;

3. VlanConfig:初始化时安装的EFI_VLAN_CONFIG_PROTOCOL;

4. NumberOfVlan:Vlan的个数;

5. MacString:网卡的MAC地址字符串;

6. Snp:对应的EFI_SIMPLE_NETWORK_PROTOCOL;

7. ServiceList:指向MNP_SERVICE_DATA结构体,每个不同的Vlan对应一个这样的结构体,不存在Vlan也算一个,只是Vlan和Priority的值都是0;

8. ConfiguredChildrenNumber:当MNP配置好之后(会对应到一个MNP_INSTANCE_DATA结构体),在调用MnpStart()时会自增1,MnpStop()时自减一,表示的是实际可用于收发数据的单元个数;

9. GroupAddressListGroupAddressCount:保存一组组的MAC地址,用来管理多播MAC;

10. FreeTxBufListAllTxBufListTxBufCount:管理一段内存,用于发数据,最终会在MnpTransmit()函数中用到;

11. FreeNbufQueNbufCntRxNbufCache:管理一段内存,用于收数据,最终会在MnpPoll()函数中用到;

12. PollTimer:在MnpInitializeDeviceData()会创建这个事件,在MnpStart()中会为它设置定时调用;

13. EnableSystemPoll:与PollTimer配合使用:

  if (MnpDeviceData->EnableSystemPoll ^ EnableSystemPoll) {
    //
    // The EnableSystemPoll differs with the current state, disable or enable
    // the system poll.
    //
    TimerOpType = EnableSystemPoll ? TimerPeriodic : TimerCancel;

    Status      = gBS->SetTimer (MnpDeviceData->PollTimer, TimerOpType, MNP_SYS_POLL_INTERVAL);
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "MnpStart: gBS->SetTimer for PollTimer failed, %r.\n", Status));

      goto ErrorExit;
    }

    MnpDeviceData->EnableSystemPoll = EnableSystemPoll;
  }

14. TimeoutCheckTimer:一个超时事件,不确定是什么东西超时...

15. MediaDetectTimer:周期性检查网线是否在位,调用的是Snp->GetStatus();

16. UnicastCountBroadcastCountMulticastCountPromiscuousCount:根据EFI_MANAGED_NETWORK_CONFIG_DATA得到的ReceiveFilters数目,通过它来设置SNP的过滤设置;

17. BufferLengthPaddingSize:代码中的说明:The size of the data buffer in the MNP_PACKET_BUFFER used to store a packet.

MNP_INSTANCE_DATA structure

MNP_INSTANCE_DATA定义在MnpImpl.h中,如下所示:

typedef struct {
  UINT32                          Signature;

  MNP_SERVICE_DATA                *MnpServiceData;

  EFI_HANDLE                      Handle;

  LIST_ENTRY                      InstEntry;

  EFI_MANAGED_NETWORK_PROTOCOL    ManagedNetwork;

  BOOLEAN                         Configured;
  BOOLEAN                         Destroyed;

  LIST_ENTRY                      GroupCtrlBlkList;

  NET_MAP                         RxTokenMap;

  LIST_ENTRY                      RxDeliveredPacketQueue;
  LIST_ENTRY                      RcvdPacketQueue;
  UINTN                           RcvdPacketQueueSize;

  EFI_MANAGED_NETWORK_CONFIG_DATA ConfigData;

  UINT8                           ReceiveFilter;
} MNP_INSTANCE_DATA;

这些值的介绍如下:

1. Signature:它的值是SIGNATURE_32 ('M', 'n', 'p', 'I');

2. MnpServiceData:对应的MNP_SERVICE_DATA结构体;

3. HandleManagedNetwork:实际安装的EFI_MANAGED_NETWORK_PROTOCOL对应的Handle和Protocol;

4. InstEntry:与MNP_SERVICE_DATA结构体中的ChildrenLis成员连接;对之前的拓扑图可以进一步扩展:

5. ConfiguredConfigData:MNP需要经过配置后才能使用(也就是说只有Configure之后才能Transmit或Receive),这两个是相关的配置参数;

6. Destroyed:关于它,代码中有说明:

  //
  // MnpServiceBindingDestroyChild may be called twice: first called by
  // MnpServiceBindingStop, second called by uninstalling the MNP protocol
  // in this ChildHandle. Use destroyed to make sure the resource clean code
  // will only excecute once.
  //

7. GroupCtrlBlkList:指向多播MAC地址的链表,用来管理多播地址;

8. RxTokenMap:这个成员是用来存放称为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;

需要注意,这里只是举了一个例子,即MNP下的一个Token格式,这种格式的Token还有很多不同的名字,比如

EFI_MANAGED_NETWORK_COMPLETION_TOKEN

EFI_IP4_COMPLETION_TOKEN

等等,它们都是这种格式的,包含一个事件,一个状态和一个指针指向接收数据或者发送数据。

RxTokenMap首先会在

NetMapInit (&Instance->RxTokenMap);

中进行初始化,这个函数包含在MnpServiceBindingCreateChild()中,它在创建MNP_INSTANCE_DATA的时候就会初始化,并在MnpServiceBindingDestroyChild()中清空RxTokenMap。

在MnpReceive()函数中,会向RxTokenMap中插入一个个的Token。

需要注意,Mnp的Receive()函数会被上层接口调用,因此上层接口的Token也就并放到了这个RxTokenMap中去。

比如下面ARP的Start()函数中有如下的代码:

  //
  // OK, start to receive arp packets from Mnp.
  //
  Status = ArpService->Mnp->Receive (ArpService->Mnp, &ArpService->RxToken);

这里的ArpService->RxToken的类型就是EFI_MANAGED_NETWORK_COMPLETION_TOKEN,它在ArpCreateService()函数中创建:

  //
  // Create the event used in the RxToken.
  //
  Status = gBS->CreateEvent (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  ArpOnFrameRcvd,
                  ArpService,
                  &ArpService->RxToken.Event
                  );

在MnpCancel()函数中,会释放这些Token。

而最重要的,这些Token的使用,位于MnpInstanceDeliverPacket()函数中。

  //
  // Get the receive token from the RxTokenMap.
  //
  RxToken = NetMapRemoveHead (&Instance->RxTokenMap, NULL);

  //
  // Signal this token's event.
  //
  RxToken->Packet.RxData  = &RxDataWrap->RxData;
  RxToken->Status         = EFI_SUCCESS;
  gBS->SignalEvent (RxToken->Event);

在这个函数中,就是一个收取数据,然后通过回调,使用Token中的事件来处理这些收到的数据。

MnpInstanceDeliverPacket()会在两个地方被调用:

一个就是前面说的Receive(),这个函数是用来注册Token的,注册完了紧接着就收取数据来处理下,当然不一定当下就能够收到数据;

所以就有了第二个地方,它的调用栈如下:

MnpInstanceDeliverPacket() <--- MnpDeliverPacket() <-- MnpReceivePacket() <--- MnpSystemPoll()

而MnpSystemPoll()是一个定时事件的回调函数,所以就有了一个定时接收数据的过程

需要注意下面的代码:

  //
  // Get the receive token from the RxTokenMap.
  //
  RxToken = NetMapRemoveHead (&Instance->RxTokenMap, NULL);

一个Token只会被MNP回调一次,之后就删除了

整个过程简单来说就是一个注册事件并执行的过程,被执行的前提是MNP收到数据。

9. RxDeliveredPacketQueue

10. RcvdPacketQueueRcvdPacketQueueSize

11. ReceiveFilter:BIT表示的过滤配置,目前的取值可以使MNP_RECEIVE_UNICAST、MNP_RECEIVE_BROADCAST中的一种或多种;

MNP Interface

下面是MNP提供的接口:

struct _EFI_MANAGED_NETWORK_PROTOCOL {
  EFI_MANAGED_NETWORK_GET_MODE_DATA       GetModeData;
  EFI_MANAGED_NETWORK_CONFIGURE           Configure;
  EFI_MANAGED_NETWORK_MCAST_IP_TO_MAC     McastIpToMac;
  EFI_MANAGED_NETWORK_GROUPS              Groups;
  EFI_MANAGED_NETWORK_TRANSMIT            Transmit;
  EFI_MANAGED_NETWORK_RECEIVE             Receive;
  EFI_MANAGED_NETWORK_CANCEL              Cancel;
  EFI_MANAGED_NETWORK_POLL                Poll;
};

extern EFI_GUID gEfiManagedNetworkServiceBindingProtocolGuid;
extern EFI_GUID gEfiManagedNetworkProtocolGuid;

另外需要注意几点:

1)网卡驱动本身也是可以处理VLAN的,这种情况下,MNP就不需要使用到Service Binding Protocol了;

2)由于上层网络协议需要使用到MNP,因此它们也像MNP一样需要提供Service Binding Protocol。

下面是目前所有提供的Service Binding Protocol:

• EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL
• EFI_ARP_SERVICE_BINDING_PROTOCOL
• EFI_EAP_SERVICE_BINDING_PROTOCOL
• EFI_IP4_SERVICE_BINDING_PROTOCOL
• EFI_TCP4_SERVICE_BINDING_PROTOCOL
• EFI_UDP4_SERVICE_BINDING_PROTOCOL
• EFI_MTFTP4_SERVICE_BINDING_PROTOCOL
• EFI_DHCP4_SERVICE_BINDING_PROTOCOL

关于VLAN的接口也在这里列出:

///
/// EFI_VLAN_CONFIG_PROTOCOL
/// provide manageability interface for VLAN setting. The intended 
/// VLAN tagging implementation is IEEE802.1Q.
///
struct _EFI_VLAN_CONFIG_PROTOCOL {
  EFI_VLAN_CONFIG_SET              Set;
  EFI_VLAN_CONFIG_FIND             Find;
  EFI_VLAN_CONFIG_REMOVE           Remove;
};

extern EFI_GUID gEfiVlanConfigProtocolGuid;

关于Manager Network Service BindingProtocol和Manager Network Protocol还需要进一步的说明。

在MNP的Start函数,即MnpDriverBindingStart()中并没有安装gEfiManagedNetworkProtocolGuid,而是在作为Service Binding Protocol的mMnpServiceBindingProtocol中的CreateChild接口中安装gEfiManagedNetworkProtocolGuid的。

因此,在使用MNP的时候,步骤大致如下:

1. 通过gEfiManagedNetworkServiceBindingProtocolGuid获取到EFI_SERVICE_BINDING_PROTOCOL;

2. 通过EFI_SERVICE_BINDING_PROTOCOL的CreateChild创建一个子Handle,在这个创建的子Handle中已经安装了gEfiManagedNetworkProtocolGuid,且它会对应到一个特定的VLAN上;

3. 通过这个子Handle就可以获取EFI_MANAGED_NETWORK_PROTOCOL;

4. 然后就可以通过EFI_MANAGED_NETWORK_PROTOCOL来使用它的接口函数。

下面是一个示例:

  //
  // Create a MNP child instance.
  // 输出的子Handle:MnpChildHandle
  //
  Status = NetLibCreateServiceChild (
             ControllerHandle,
             ImageHandle,
             &gEfiManagedNetworkServiceBindingProtocolGuid,
             &ArpService->MnpChildHandle
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Open the MNP protocol.
  //
  Status = gBS->OpenProtocol (
                  ArpService->MnpChildHandle,
                  &gEfiManagedNetworkProtocolGuid,
                  (VOID **)&ArpService->Mnp,
                  ImageHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    goto ERROR_EXIT;
  }

  //
  // Get the underlayer Snp mode data.
  //  这里开始使用MNP了
  //
  Status = ArpService->Mnp->GetModeData (ArpService->Mnp, NULL, &ArpService->SnpMode);

最后说明下,同SNP一样,初始化MNP之后网卡并没有真正的运行起来。MNP需要经过Configure()之后才会设置定时器,定时来获取数据并处理。

另外,SNP的接口相对简单,而MNP的接口会比较复杂,且后续的协议都需要用到MNP的接口,因此后面还会经常提到MNP及其接口。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值