本文主要是介绍【UEFI基础】EDK网络框架(SNP),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
SNP
SNP代码综述
SNP全称是Simple Network Protocol,它是EDK代码中能够控制的最底层的网络接口。该模块的作用有以下的几个部分:
-
网卡操作,比如初始化网卡,打开/关闭网口等。
-
提供数据的底层传输接口,供上层协议使用。
SNP驱动依赖于UNDI驱动,即网卡驱动。该驱动执行后会安装gEfiNetworkInterfaceIdentifierProtocolGuid_31
对应的协议。
SNP驱动执行时就会去查找该协议是否存在,如果存在,就会据此构造SNP_DRIVER
结构体来表示这张网卡。后续对该网卡的操作都是通过SNP_DRIVER
结构体及其中的EFI_SIMPLE_NETWORK_PROTOCOL
来完成。
SNP是一个UEFI Driver Model(事实上几乎所有的UEFI网络驱动都是),所以会安装EFI_DRIVER_BINDING_PROTOCOL
,代码如下:
EFI_DRIVER_BINDING_PROTOCOL gSimpleNetworkDriverBinding = {SimpleNetworkDriverSupported,SimpleNetworkDriverStart,SimpleNetworkDriverStop,0xa,NULL,NULL
};
后续将以上述的Supported和Start函数为线索介绍其初始化流程,其对应模块NetworkPkg\SnpDxe\SnpDxe.inf。
SNP在UEFI网络协议栈中的关系图:
SimpleNetworkDriverSupported
Supported函数主要是判断驱动是否需要执行,它主要是寻找依赖的Protocol是否已经安装,SNP的初始化函数(即Start函数)执行依赖于以下的部分:
- 是否安装了Device Path Protocol:
Status = gBS->OpenProtocol (Controller,&gEfiDevicePathProtocolGuid,NULL,This->DriverBindingHandle,Controller,EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
- 判断NII是否已经安装,且类型是否支持:
Status = gBS->OpenProtocol (Controller,&gEfiNetworkInterfaceIdentifierProtocolGuid_31,(VOID **)&NiiProtocol,This->DriverBindingHandle,Controller,EFI_OPEN_PROTOCOL_BY_DRIVER);//// check the version, we don't want to connect to the undi16//if (NiiProtocol->Type != EfiNetworkInterfaceUndi) {Status = EFI_UNSUPPORTED;goto Done;}
- 判断
!PXE
结构体是否正常:
//// Check to see if !PXE structure is valid. Paragraph alignment of !PXE structure is required.//if ((NiiProtocol->Id & 0x0F) != 0) {DEBUG ((DEBUG_NET, "\n!PXE structure is not paragraph aligned.\n"));Status = EFI_UNSUPPORTED;goto Done;}Pxe = (PXE_UNDI *)(UINTN)(NiiProtocol->Id);//// Verify !PXE revisions.//if (Pxe->hw.Signature != PXE_ROMID_SIGNATURE) {DEBUG ((DEBUG_NET, "\n!PXE signature is not valid.\n"));Status = EFI_UNSUPPORTED;goto Done;}if (Pxe->hw.Rev < PXE_ROMID_REV) {DEBUG ((DEBUG_NET, "\n!PXE.Rev is not supported.\n"));Status = EFI_UNSUPPORTED;goto Done;}if (Pxe->hw.MajorVer < PXE_ROMID_MAJORVER) {DEBUG ((DEBUG_NET, "\n!PXE.MajorVer is not supported.\n"));Status = EFI_UNSUPPORTED;goto Done;} else if ((Pxe->hw.MajorVer == PXE_ROMID_MAJORVER) && (Pxe->hw.MinorVer < PXE_ROMID_MINORVER)) {DEBUG ((DEBUG_NET, "\n!PXE.MinorVer is not supported."));Status = EFI_UNSUPPORTED;goto Done;}//// Do S/W UNDI specific checks.//if ((Pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) == 0) {if (Pxe->sw.EntryPoint < Pxe->sw.Len) {DEBUG ((DEBUG_NET, "\n!PXE S/W entry point is not valid."));Status = EFI_UNSUPPORTED;goto Done;}if (Pxe->sw.BusCnt == 0) {DEBUG ((DEBUG_NET, "\n!PXE.BusCnt is zero."));Status = EFI_UNSUPPORTED;goto Done;}}
实际上SNP还需要依赖于gEfiPciIoProtocolGuid
,不过并没有在Supported函数中体现。
SimpleNetworkDriverStart
SNP初始化主要做了以下的是事情:
- 获取
EFI_PCI_IO_PROTOCOL
。 - 获取
EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL
。 - 从
EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL
中得到PXE_UNDI
结构体,通过检查其Checksum来确定有效性,还会判断其中的Implementation
属性。 - 使用
EFI_PCI_IO_PROTOCOL
分配资源:
// 分配资源给SNP_DRIVER,它是一个全局的结构体,每个网卡都有一个。Status = PciIo->AllocateBuffer (PciIo,AllocateAnyPages,EfiBootServicesData,SNP_MEM_PAGES (sizeof (SNP_DRIVER)),&Address,0);// 分配内存给CPB和DB,它们是SNP和UNDI交互的缓冲区,初始化完成之后这部分内存会被释放掉。Status = PciIo->AllocateBuffer (PciIo,AllocateAnyPages,EfiBootServicesData,SNP_MEM_PAGES (4096),&Address,0);
-
初始化
SNP_DRIVER
结构体,该结构体会在后面进一步说明。 -
执行必要的UNDI调用,完成信息获取,对应操作码:
-
PXE_OPCODE_START
-
PXE_OPCODE_GET_INIT_INFO
-
PXE_OPCODE_STATION_ADDRESS
-
-
进一步初始化
SNP_DRIVER
结构体。 -
初始化完毕之后,还会执行UNDI调用,用于暂时关闭网卡(直到后续真正使用时再打开),对应操作码:
PXE_OPCODE_SHUTDOWN
PXE_OPCODE_STOP
从这里可以看到,在完成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的网络收发并不能使用,而开启、关闭和传输的操作依赖于更上层的网络栈驱动代码。
-
创建ExitBootServicesEvent事件,也是执行上述的关闭操作,主要是为了在退出UEFI之前关闭网络,避免SNP驱动可能的DMA写坏了OS启动需要的内存空间。
-
最后安装
gEfiSimpleNetworkProtocolGuid
对应的Protocol,供上层接口使用。
对于初始化SNP来说,最重要的有以下几点:
SNP_DRIVER
结构体初始化。- 安装
EFI_SIMPLE_NETWORK_PROTOCOL
接口。
后面将进一步说明。
SNP_DRIVER
SNP_DRIVER
定义在NetworkPkg\SnpDxe\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;
内容较多,这里只对其中比较重要的成员进行介绍。
Lock
:是UEFI下的锁,在SNP初始化的Start函数中有锁的初始化:
EfiInitializeLock (&Snp->Lock, TPL_NOTIFY);
这个锁最终也不会在SNP代码中使用,实际上它会传递给UNDI,这主要通过传递回调函数SnpUndi32CallbackBlock()
来实现:
VOID
EFIAPI
SnpUndi32CallbackBlock (IN UINT64 UniqueId,IN UINT32 Enable)
{SNP_DRIVER *Snp;Snp = (SNP_DRIVER *)(UINTN)UniqueId;//// tcpip was calling snp at tpl_notify and when we acquire a lock that was// created at a lower level (TPL_CALLBACK) it gives an assert!//if (Enable != 0) {EfiAcquireLock (&Snp->Lock);} else {EfiReleaseLock (&Snp->Lock);}
}// 传递给UDNI
Cpb31->Block = (UINT64)(UINTN)&SnpUndi32CallbackBlock;
在UNDI的CPB结构体包含的基础操作中就有Block的操作,用来处理多线程/多核的情况。至于UNDI中具体如何使用,可以在GigUndiDxe找到部分代码,不过我们不关注网卡驱动的实现。
-
Snp
:这个就是EFI_SIMPLE_NETWORK_PROTOCOL
,后面会详细介绍它的所有成员。 -
Mode
:它对应的结构体是EFI_SIMPLE_NETWORK_MODE,后面会进一步介绍。 -
DeviceHandle
:每个SNP_DRIVER
结构体对应一个设备,所以也就对应到一个Handle上,不过代码中似乎并没有实际用到。 -
DevicePath
:同上,每个SNP_DRIVER
结构体也对应到一个DevicePath上,也没有用到。 -
IssueUndi32Command
:这个是UEFI下的执行函数,通过它可以调用UNDI中的接口,可以有软件和硬件两种形式,不过SNP目前只实现了软件形式的。 -
IsSwUndi
:如果是软件形式的UNDI,就设置为TRUE。 -
IfNum
:表示一个!PXE
结构体控制的网卡数目。 -
TxRxBufferSize
,TxRxBuffer
:这两个值确定一段内存空间,是UNDI需要使用的。UEFI通过UNDI的PXE_OPCODE_GET_INIT_INFO
操作获取UNDI的初始化信息(即TxRxBufferSize
),再通过这个初始化信息来分配一段内容空间(由TxRxBuffer
指定),在SNP中调用PxeInit()
函数时,会将TxRxBuffer
放到Cpb
中传递给UNDI。至于这段内存空间到底是干什么的,从名字上看应该是UNDI收发数据的缓存区。 -
PciIo
,IoBarIndex
,MemoryBarIndex
:前面已经说过一个SNP_DRIVER
对应一个网卡,所以这里的PciIo
就是用来访问该设备的接口,通过它也可以确定IoBarIndex
和MemoryBarIndex
,这两个参数指定了网卡在系统中映射的资源空间。 -
Cdb
,Cpb
,CpbUnmap
,Db
:CpbUnmap
并没有使用,剩下的三个参数是UNDI调用需要的参数。Cdb
中放置的数据表示需要进行什么操作,Cpb
表示操作需要的参数,Db
存放UNDI调用返回的数据。 -
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);
ExitBootServicesEvent
:一个事件,在UEFI调用gBS->ExitBootServices()
是调用,该事件最终会关闭网卡。MediaStatusSupported
:如果UNDI支持上报网卡的连接信息,则为TRUE。CableDetectSupported
:如果UNDI支持测试网线是否连接,则为TRUE。
EFI_SIMPLE_NETWORK_MODE
该结构体位于MdePkg\Include\Protocol\SimpleNetwork.h,其结构体代码如下:
typedef struct {////// Reports the current state of the network interface.///UINT32 State;////// The size, in bytes, of the network interface's HW address.///UINT32 HwAddressSize;////// The size, in bytes, of the network interface's media header.///UINT32 MediaHeaderSize;////// The maximum size, in bytes, of the packets supported by the network interface.///UINT32 MaxPacketSize;////// The size, in bytes, of the NVRAM device attached to the network interface.///UINT32 NvRamSize;////// The size that must be used for all NVRAM reads and writes. The/// start address for NVRAM read and write operations and the total/// length of those operations, must be a multiple of this value. The/// legal values for this field are 0, 1, 2, 4, and 8.///UINT32 NvRamAccessSize;////// The multicast receive filter settings supported by the network interface.///UINT32 ReceiveFilterMask;////// The current multicast receive filter settings.///UINT32 ReceiveFilterSetting;////// The maximum number of multicast address receive filters supported by the driver.///UINT32 MaxMCastFilterCount;////// The current number of multicast address receive filters.///UINT32 MCastFilterCount;////// Array containing the addresses of the current multicast address receive filters.///EFI_MAC_ADDRESS MCastFilter[MAX_MCAST_FILTER_CNT];////// The current HW MAC address for the network interface.///EFI_MAC_ADDRESS CurrentAddress;////// The current HW MAC address for broadcast packets.///EFI_MAC_ADDRESS BroadcastAddress;////// The permanent HW MAC address for the network interface.///EFI_MAC_ADDRESS PermanentAddress;////// The interface type of the network interface.///UINT8 IfType;////// TRUE if the HW MAC address can be changed.///BOOLEAN MacAddressChangeable;////// TRUE if the network interface can transmit more than one packet at a time.///BOOLEAN MultipleTxSupported;////// TRUE if the presence of media can be determined; otherwise FALSE.///BOOLEAN MediaPresentSupported;////// TRUE if media are connected to the network interface; otherwise FALSE.///BOOLEAN MediaPresent;
} EFI_SIMPLE_NETWORK_MODE;
该结构体是SNP_DRIVER
的成员,同时也是EFI_SIMPLE_NETWORK_PROTOCOL
中的成员,两者是一致的,指向的是同一个内容。至于每个成员表示什么,从注释上基本能够看出来,这里不再一一介绍。
在SimpleNetworkDriverStart()
函数中会初始化该结构体中的大部分成员:
EFI_STATUS
EFIAPI
SimpleNetworkDriverStart (IN EFI_DRIVER_BINDING_PROTOCOL *This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
{//// Initialize simple network protocol mode structure//Snp->Mode.State = EfiSimpleNetworkStopped;Snp->Mode.HwAddressSize = Snp->InitInfo.HWaddrLen;Snp->Mode.MediaHeaderSize = Snp->InitInfo.MediaHeaderLen;Snp->Mode.MaxPacketSize = Snp->InitInfo.FrameDataLen;Snp->Mode.NvRamAccessSize = Snp->InitInfo.NvWidth;Snp->Mode.NvRamSize = Snp->InitInfo.NvCount * Snp->Mode.NvRamAccessSize;Snp->Mode.IfType = Snp->InitInfo.IFtype;Snp->Mode.MaxMCastFilterCount = Snp->InitInfo.MCastFilterCnt;Snp->Mode.MCastFilterCount = 0;if (Snp->CableDetectSupported || Snp->MediaStatusSupported) {Snp->Mode.MediaPresentSupported = TRUE;}if ((Pxe->hw.Implementation & PXE_ROMID_IMP_STATION_ADDR_SETTABLE) != 0) {Snp->Mode.MacAddressChangeable = TRUE;} else {Snp->Mode.MacAddressChangeable = FALSE;}if ((Pxe->hw.Implementation & PXE_ROMID_IMP_MULTI_FRAME_SUPPORTED) != 0) {Snp->Mode.MultipleTxSupported = TRUE;} else {Snp->Mode.MultipleTxSupported = FALSE;}Snp->Mode.ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) != 0) {Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;}if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED) != 0) {Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;}if ((Pxe->hw.Implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED) != 0) {Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;}if ((Pxe->hw.Implementation & PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED) != 0) {Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;}if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) != 0) {Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;}Snp->Mode.ReceiveFilterSetting = 0;Snp->Mode.MediaPresent = FALSE;
}
这些数据大部分来自UNDI。
EFI_SIMPLE_NETWORK_PROTOCOL
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.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;Snp->Snp.Start = SnpUndi32Start;Snp->Snp.Stop = SnpUndi32Stop;Snp->Snp.Initialize = SnpUndi32Initialize;Snp->Snp.Reset = SnpUndi32Reset;Snp->Snp.Shutdown = SnpUndi32Shutdown;Snp->Snp.ReceiveFilters = SnpUndi32ReceiveFilters;Snp->Snp.StationAddress = SnpUndi32StationAddress;Snp->Snp.Statistics = SnpUndi32Statistics;Snp->Snp.MCastIpToMac = SnpUndi32McastIpToMac;Snp->Snp.NvData = SnpUndi32NvData;Snp->Snp.GetStatus = SnpUndi32GetStatus;Snp->Snp.Transmit = SnpUndi32Transmit;Snp->Snp.Receive = SnpUndi32Receive;Snp->Snp.WaitForPacket = NULL;
后面会介绍这些函数的实现。
Snp.Start
对应的实现是SnpUndi32Start()
,其代码如下:
EFI_STATUS
PxeStart (IN SNP_DRIVER *Snp)
{PXE_CPB_START_31 *Cpb31;Cpb31 = Snp->Cpb;//// Initialize UNDI Start CDB for H/W UNDI//Snp->Cdb.OpCode = PXE_OPCODE_START;Snp->Cdb.OpFlags = PXE_OPFLAGS_NOT_USED;Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;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;//// Make changes to H/W UNDI Start CDB if this is// a S/W UNDI.//if (Snp->IsSwUndi) {Snp->Cdb.CPBsize = (UINT16)sizeof (PXE_CPB_START_31);Snp->Cdb.CPBaddr = (UINT64)(UINTN)Cpb31;Cpb31->Delay = (UINT64)(UINTN)&SnpUndi32CallbackDelay;Cpb31->Block = (UINT64)(UINTN)&SnpUndi32CallbackBlock;//// Virtual == Physical. This can be set to zero.//Cpb31->Virt2Phys = (UINT64)(UINTN)0;Cpb31->Mem_IO = (UINT64)(UINTN)&SnpUndi32CallbackMemio;Cpb31->Map_Mem = (UINT64)(UINTN)&SnpUndi32CallbackMap;Cpb31->UnMap_Mem = (UINT64)(UINTN)&SnpUndi32CallbackUnmap;Cpb31->Sync_Mem = (UINT64)(UINTN)&SnpUndi32CallbackSync;Cpb31->Unique_ID = (UINT64)(UINTN)Snp;}(*Snp->IssueUndi32Command)((UINT64)(UINTN)&Snp->Cdb);//// Set simple network state to Started and return success.//Snp->Mode.State = EfiSimpleNetworkStarted;
}EFI_STATUS
EFIAPI
SnpUndi32Start (IN EFI_SIMPLE_NETWORK_PROTOCOL *This)
{Status = PxeStart (Snp);
}
其它的实现函数也都类似,大致流程如下:
- 状态判断,SNP的状态都在
EFI_SIMPLE_NETWORK_MODE
这个结构体中。 - 构建CPB、CDB、DB等需要传递给UNDI的数据结构。
- UNDI调用:
(*Snp->IssueUndi32Command)((UINT64)(UINTN)&Snp->Cdb);
- 调用执行后对返回数据的判断,也在CDB中。如果有返回数据,则在DB中。
EFI_SIMPLE_NETWORK_PROTOCOL
中的其它接口基本都是按照这样的形式来完成操作的,这里不再多介绍。
SNP代码示例
网络应用程序编写中通常不会直接使用SNP,因为它发送的是RAW数据,并没有网络协议相关的数据信息,所以即使发送出去也不会被正常解析。
不过我们也可以对RAW数据进行自己的编码,让它成为有效的数据,然后通过Snp->Transmit()
发送,在NetworkPkg\Library\DxeNetLib\DxeNetLib.c中的NetDebugOutput()
就是一个示例,下面是示例代码(位于BeniPkg\DynamicCommand\TestDynamicCommand\TestSnp.c):
NET_DEBUG_ERROR ("TestSnp", ("This is a test!"));
这里的NET_DEBUG_ERROR
是一个宏,它调用了NetDebugOutput()
:
#define NET_DEBUG_ERROR(Module, PrintArg) \NetDebugOutput ( \NETDEBUG_LEVEL_ERROR, \Module, \__FILE__, \DEBUG_LINE_NUMBER, \NetDebugASPrint PrintArg \)
编译后启动BIOS,在Shell下执行test snp
,这样就发送了数据。为了能够抓取到这个数据,需要使用Wireshark工具。在主机上安装该工具,打开后选择tap0,开始抓取数据:
BIOS Shell下执行命令之后,得到如下的数据:
通过代码构建的Syslog包可以被Wireshark抓到,可以看到其数据与代码中填入的一致。
这篇关于【UEFI基础】EDK网络框架(SNP)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!