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. ServiceHandle、ServiceBinding:gEfiManagedNetworkServiceBindingProtocolGuid对应的Protocol和Handle,每一个MNP_SERVICE_DATA结构都对应一个,是在MnpCreateServiceData()函数中安装的;
5. DevicePath:它的值就是在原来网卡的DevicePath上再添加VLAN的部分,具体可以参考MnpCreateVlanChild()函数;
6. ChildrenList、ChildrenNumber:这两个值涉及到EFI_SERVICE_BINDING_PROTOCOL的CreateChild()函数创建的实例,这里就是MNP_INSTANCE_DATA结构体(第三个重要的结构体),通过这两个成员可以访问到MNP_INSTANCE_DATA结构体,它包含了EFI_MANAGED_NETWORK_PROTOCOL成员,这才是收发数据的主体;
7. Mtu:最大传输单元会因为Vlan的引入而减少几个字节(一般是4个字节);
8. VlanId、Priority: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. ControllerHandle、ImageHandle:对应到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. GroupAddressList、GroupAddressCount:保存一组组的MAC地址,用来管理多播MAC;
10. FreeTxBufList、AllTxBufList、TxBufCount:管理一段内存,用于发数据,最终会在MnpTransmit()函数中用到;
11. FreeNbufQue、NbufCnt、RxNbufCache:管理一段内存,用于收数据,最终会在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. UnicastCount、BroadcastCount、MulticastCount、PromiscuousCount:根据EFI_MANAGED_NETWORK_CONFIG_DATA得到的ReceiveFilters数目,通过它来设置SNP的过滤设置;
17. BufferLength、PaddingSize:代码中的说明: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. Handle、ManagedNetwork:实际安装的EFI_MANAGED_NETWORK_PROTOCOL对应的Handle和Protocol;
4. InstEntry:与MNP_SERVICE_DATA结构体中的ChildrenLis成员连接;对之前的拓扑图可以进一步扩展:
5. Configured、ConfigData: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. RcvdPacketQueue、RcvdPacketQueueSize:
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及其接口。