SNP
SNP全称是Simple Network Protocol。
该模块的作用有以下的几个部分:
1. 网卡操作,比如初始化网卡,打开/关闭网口等;
2. 提供数据的传输接口,供上层协议使用;
SNP驱动依赖于UNDI/NII驱动,即网卡驱动。该驱动执行后会安装gEfiNetworkInterfaceIdentifierProtocolGuid_31对应的协议。
SNP驱动执行时就会去查看该协议是否存在,如果存在,就会据此构造SNP_DRIVER结构体来表示这张网卡。
后续对该网卡的操作都是通过SNP_DRIVER结构体,及其中的SNP来完成。
SNP驱动的初始化主要做了以下的是事情:
1. 初始化SNP_DRIVER结构体;
2. 创建ExitBootServicesEvent事件,是为了在退出UEFI之前关闭网络,避免网络的DMA写坏了OS启动需要的内存空间;
3. 安装gEfiSimpleNetworkProtocolGuid对应的Protocol,供上层接口使用;
所以对于SNP来说,最重要的其实是两点,一个是SNP_DRIVER这个结构体,另一个是SNP提供的接口。
SNP_DRIVER structure
SNP_DRIVER定义在Snp.h文件中,如下所示:
typedef struct {
UINT32 Signature;
EFI_LOCK Lock;
EFI_SIMPLE_NETWORK_PROTOCOL Snp;
EFI_SIMPLE_NETWORK_MODE Mode;
EFI_HANDLE DeviceHandle;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
//
// Local instance data needed by SNP driver
//
// Pointer to S/W UNDI API entry point
// This will be NULL for H/W UNDI
//
ISSUE_UNDI32_COMMAND IssueUndi32Command;
BOOLEAN IsSwUndi;
//
// undi interface number, if one undi manages more nics
//
PXE_IFNUM IfNum;
//
// Allocated tx/rx buffer that was passed to UNDI Initialize.
//
UINT32 TxRxBufferSize;
VOID *TxRxBuffer;
//
// mappable buffers for receive and fill header for undi3.0
// these will be used if the user buffers are above 4GB limit (instead of
// mapping the user buffers)
//
UINT8 *ReceiveBufffer;
VOID *ReceiveBufferUnmap;
UINT8 *FillHeaderBuffer;
VOID *FillHeaderBufferUnmap;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT8 IoBarIndex;
UINT8 MemoryBarIndex;
//
// Buffers for command descriptor block, command parameter block
// and data block.
//
PXE_CDB Cdb;
VOID *Cpb;
VOID *CpbUnmap;
VOID *Db;
//
// UNDI structure, we need to remember the init info for a long time!
//
PXE_DB_GET_INIT_INFO InitInfo;
VOID *SnpDriverUnmap;
//
// when ever we map an address, we must remember it's address and the un-map
// cookie so that we can unmap later
//
struct MAP_LIST {
EFI_PHYSICAL_ADDRESS VirtualAddress;
VOID *MapCookie;
} MapList[MAX_MAP_LENGTH];
EFI_EVENT ExitBootServicesEvent;
//
// Whether UNDI support reporting media status from GET_STATUS command,
// i.e. PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED or
// PXE_STATFLAGS_GET_STATUS_NO_MEDIA_NOT_SUPPORTED
//
BOOLEAN MediaStatusSupported;
//
// Whether UNDI support cable detect for INITIALIZE command,
// i.e. PXE_STATFLAGS_CABLE_DETECT_SUPPORTED or
// PXE_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED
//
BOOLEAN CableDetectSupported;
//
// Array of the recycled transmit buffer address from UNDI.
//
UINT64 *RecycledTxBuf;
//
// The maximum number of recycled buffer pointers in RecycledTxBuf.
//
UINT32 MaxRecycledTxBuf;
//
// Current number of recycled buffer pointers in RecycledTxBuf.
//
UINT32 RecycledTxBufCount;
} SNP_DRIVER;
这些值的介绍如下:
1. Signature:就是一个标识,对应的值是"sdns";
2. Lock:是UEFI下的锁。UEFI下没有多线程,所以这个锁应该是用于中断的,目前并不清楚它具体想要锁什么;
3. Snp:这个就Simple Network Protocol,下面会介绍它的所以成员;
4. Mode:在Snp中也有一个成员Mode,两者的值是一致的,后面会介绍;
5. DeviceHandle:每个SNP_DRIVER结构体对应一个设备,所以也就对应到一个Handle上;
6. DevicePath:同上,每个SNP_DRIVER结构体也对应到一个DevicePath上;
7. IssueUndi32Command:这个是UEFI下的执行函数,通过它可以调用UNDI中的接口,可以有软件和硬件两种形式,不过SNP目前只实现了软件形式的;
8. IsSwUndi:如果是软件形式的UNDI,就设置为TRUE;
9. IfNum:表示一个!PXE结构体控制的网卡数目;
10. TxRxBufferSize,TxRxBuffer:这两个值确定一段内存空间,是UNDI需要使用的。UEFI通过UNDI的PXE_OPCODE_GET_INIT_INFO操作符获取UNDI的初始化信息(即TxRxBufferSize),再通过这个初始化信息来分配一段内容空间(由TxRxBuffer指定),在SNP中调用PxeInit()函数时,会将TxRxBuffer放到Cpb中传递给UNDI。至于这段内存空间到底是干什么的,从名字上看应该是UNDI收发数据的缓存区;
11. ReceiveBufffer,ReceiveBufferUnmap,FillHeaderBuffer,FillHeaderBufferUnmap:这4个参数并没有用到;
12. PciIo,IoBarIndex,MemoryBarIndex:前面已经说过一个SNP_DRIVER对应一个网卡,所以这里的PciIo就是用来访问该设备的接口,通过它也可以确定IoBarIndex和MemoryBarIndex,这两个参数指定了网卡在系统中映射的资源空间;
13. Cdb,Cpb,CpbUnmap,Db:CpbUnmap并没有使用,剩下的三个参数是UNDI调用需要的参数,Cdb中放置的数据表示需要进行什么操作,Cpb表示操作需要的参数,Db存放的是具体需要传递的参数;
14. InitInfo:SNP调用PXE_OPCODE_GET_INIT_INFO之后会保留到这个参数中,具体的调用函数如下:
Snp->Cdb.OpCode = PXE_OPCODE_GET_INIT_INFO;
Snp->Cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
Snp->Cdb.CPBaddr = PXE_DBADDR_NOT_USED;
Snp->Cdb.DBsize = (UINT16) sizeof (Snp->InitInfo);
Snp->Cdb.DBaddr = (UINT64)(UINTN) (&Snp->InitInfo);//作为参数传入,UNDI会去填充
Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
Snp->Cdb.IFnum = Snp->IfNum;
Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
DEBUG ((EFI_D_NET, "\nSnp->undi.get_init_info() "));
(*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
15. SnpDriverUnmap:又是一个并没有使用的参数;
16. MapList:一个映射数组,似乎是给DMA用的,但是看不出来怎么样的;
17. ExitBootServicesEvent:一个事件,在UEFI调用gBS->ExitBootServices()是调用;
18. MediaStatusSupported:如果UNDI支持上报网卡的连接信息,则为TRUE;
19. CableDetectSupported:如果UNDI支持测试网线是否连接,则为TRUE;
20. RecycledTxBuf,MaxRecycledTxBuf,RecycledTxBufCount:这三个值共同管理一段内存空间,内存空间的大小是32个64位数,这段内存空间由UEFI分配,在SNP调用PxeGetStatus时会使用,现在还不知道这个函数有什么用。
以上就是所有的参数。
SNP Interface
SNP提供的接口如下:
///
/// The EFI_SIMPLE_NETWORK_PROTOCOL protocol is used to initialize access
/// to a network adapter. Once the network adapter initializes,
/// the EFI_SIMPLE_NETWORK_PROTOCOL protocol provides services that
/// allow packets to be transmitted and received.
///
struct _EFI_SIMPLE_NETWORK_PROTOCOL {
///
/// Revision of the EFI_SIMPLE_NETWORK_PROTOCOL. All future revisions must
/// be backwards compatible. If a future version is not backwards compatible
/// it is not the same GUID.
///
UINT64 Revision;
EFI_SIMPLE_NETWORK_START Start;
EFI_SIMPLE_NETWORK_STOP Stop;
EFI_SIMPLE_NETWORK_INITIALIZE Initialize;
EFI_SIMPLE_NETWORK_RESET Reset;
EFI_SIMPLE_NETWORK_SHUTDOWN Shutdown;
EFI_SIMPLE_NETWORK_RECEIVE_FILTERS ReceiveFilters;
EFI_SIMPLE_NETWORK_STATION_ADDRESS StationAddress;
EFI_SIMPLE_NETWORK_STATISTICS Statistics;
EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC MCastIpToMac;
EFI_SIMPLE_NETWORK_NVDATA NvData;
EFI_SIMPLE_NETWORK_GET_STATUS GetStatus;
EFI_SIMPLE_NETWORK_TRANSMIT Transmit;
EFI_SIMPLE_NETWORK_RECEIVE Receive;
///
/// Event used with WaitForEvent() to wait for a packet to be received.
///
EFI_EVENT WaitForPacket;
///
/// Pointer to the EFI_SIMPLE_NETWORK_MODE data for the device.
///
EFI_SIMPLE_NETWORK_MODE *Mode;
};
extern EFI_GUID gEfiSimpleNetworkProtocolGuid;
如UNDI章节所述,上面的接口都是通过UNDI来实现的。
SNP提供的数据接口是纯数据包的传输,并不涉及到具体的网络协议。
理论上我们可以自己构造网络协议并传输。
当然也可以使用SNP之上的各个模块提供的接口来传输数据。
在SNP的初始化过程中会安装gEfiSimpleNetworkProtocolGuid:
//
// add SNP to the undi handle
//
Status = gBS->InstallProtocolInterface (
&Controller,
&gEfiSimpleNetworkProtocolGuid,
EFI_NATIVE_INTERFACE,
&(Snp->Snp)
);
另外,在完成SNP的初始化之后,网络设备并没有一直打开:
//
// We should not leave UNDI started and initialized here. this DriverStart()
// routine must only find and attach the SNP interface to UNDI layer that it
// finds on the given handle!
// The UNDI layer will be started when upper layers call Snp->start.
// How ever, this DriverStart() must fill up the snp mode structure which
// contains the MAC address of the NIC. For this reason we started and
// initialized UNDI here, now we are done, do a shutdown and stop of the
// UNDI interface!
//
PxeShutdown (Snp);
PxeStop (Snp);
所以在SNP被初始化完成之后,UEFI的网络收发并没有被打开。