【UEFI基础】EDK网络框架(SNP)

2024-01-05 04:04
文章标签 基础 框架 网络 snp uefi edk

本文主要是介绍【UEFI基础】EDK网络框架(SNP),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SNP

SNP代码综述

SNP全称是Simple Network Protocol,它是EDK代码中能够控制的最底层的网络接口。该模块的作用有以下的几个部分:

  1. 网卡操作,比如初始化网卡,打开/关闭网口等。

  2. 提供数据的底层传输接口,供上层协议使用。

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网络协议栈中的关系图:

支持
提供
支持
支持
提供
gEfiPciIoProtocolGuid
UNDI
gEfiNetworkInterfaceIdentifierProtocolGuid_31
gEfiDevicePathProtocolGuid
SNP
gEfiSimpleNetworkProtocolGuid

SimpleNetworkDriverSupported

Supported函数主要是判断驱动是否需要执行,它主要是寻找依赖的Protocol是否已经安装,SNP的初始化函数(即Start函数)执行依赖于以下的部分:

  1. 是否安装了Device Path Protocol:
  Status = gBS->OpenProtocol (Controller,&gEfiDevicePathProtocolGuid,NULL,This->DriverBindingHandle,Controller,EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
  1. 判断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;}
  1. 判断!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初始化主要做了以下的是事情:

  1. 获取EFI_PCI_IO_PROTOCOL
  2. 获取EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL
  3. EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL中得到PXE_UNDI结构体,通过检查其Checksum来确定有效性,还会判断其中的Implementation属性。
  4. 使用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);
  1. 初始化SNP_DRIVER结构体,该结构体会在后面进一步说明。

  2. 执行必要的UNDI调用,完成信息获取,对应操作码:

    • PXE_OPCODE_START

    • PXE_OPCODE_GET_INIT_INFO

    • PXE_OPCODE_STATION_ADDRESS

  3. 进一步初始化SNP_DRIVER结构体。

  4. 初始化完毕之后,还会执行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的网络收发并不能使用,而开启、关闭和传输的操作依赖于更上层的网络栈驱动代码。

  1. 创建ExitBootServicesEvent事件,也是执行上述的关闭操作,主要是为了在退出UEFI之前关闭网络,避免SNP驱动可能的DMA写坏了OS启动需要的内存空间。

  2. 最后安装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结构体控制的网卡数目。

  • TxRxBufferSizeTxRxBuffer:这两个值确定一段内存空间,是UNDI需要使用的。UEFI通过UNDI的PXE_OPCODE_GET_INIT_INFO操作获取UNDI的初始化信息(即TxRxBufferSize),再通过这个初始化信息来分配一段内容空间(由TxRxBuffer指定),在SNP中调用PxeInit()函数时,会将TxRxBuffer放到Cpb中传递给UNDI。至于这段内存空间到底是干什么的,从名字上看应该是UNDI收发数据的缓存区。

  • PciIoIoBarIndexMemoryBarIndex:前面已经说过一个SNP_DRIVER对应一个网卡,所以这里的PciIo就是用来访问该设备的接口,通过它也可以确定IoBarIndexMemoryBarIndex,这两个参数指定了网卡在系统中映射的资源空间。

  • CdbCpbCpbUnmapDbCpbUnmap并没有使用,剩下的三个参数是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);
}

其它的实现函数也都类似,大致流程如下:

  1. 状态判断,SNP的状态都在EFI_SIMPLE_NETWORK_MODE这个结构体中。
  2. 构建CPB、CDB、DB等需要传递给UNDI的数据结构。
  3. UNDI调用:
 (*Snp->IssueUndi32Command)((UINT64)(UINTN)&Snp->Cdb);
  1. 调用执行后对返回数据的判断,也在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)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/571597

相关文章

mysql的基础语句和外键查询及其语句详解(推荐)

《mysql的基础语句和外键查询及其语句详解(推荐)》:本文主要介绍mysql的基础语句和外键查询及其语句详解(推荐),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 目录一、mysql 基础语句1. 数据库操作 创建数据库2. 表操作 创建表3. CRUD 操作二、外键

Python基础语法中defaultdict的使用小结

《Python基础语法中defaultdict的使用小结》Python的defaultdict是collections模块中提供的一种特殊的字典类型,它与普通的字典(dict)有着相似的功能,本文主要... 目录示例1示例2python的defaultdict是collections模块中提供的一种特殊的字

Python基础文件操作方法超详细讲解(详解版)

《Python基础文件操作方法超详细讲解(详解版)》文件就是操作系统为用户或应用程序提供的一个读写硬盘的虚拟单位,文件的核心操作就是读和写,:本文主要介绍Python基础文件操作方法超详细讲解的相... 目录一、文件操作1. 文件打开与关闭1.1 打开文件1.2 关闭文件2. 访问模式及说明二、文件读写1.

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1

C#基础之委托详解(Delegate)

《C#基础之委托详解(Delegate)》:本文主要介绍C#基础之委托(Delegate),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 委托定义2. 委托实例化3. 多播委托(Multicast Delegates)4. 委托的用途事件处理回调函数LINQ

基于Flask框架添加多个AI模型的API并进行交互

《基于Flask框架添加多个AI模型的API并进行交互》:本文主要介绍如何基于Flask框架开发AI模型API管理系统,允许用户添加、删除不同AI模型的API密钥,感兴趣的可以了解下... 目录1. 概述2. 后端代码说明2.1 依赖库导入2.2 应用初始化2.3 API 存储字典2.4 路由函数2.5 应

Python GUI框架中的PyQt详解

《PythonGUI框架中的PyQt详解》PyQt是Python语言中最强大且广泛应用的GUI框架之一,基于Qt库的Python绑定实现,本文将深入解析PyQt的核心模块,并通过代码示例展示其应用场... 目录一、PyQt核心模块概览二、核心模块详解与示例1. QtCore - 核心基础模块2. QtWid

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp