【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

相关文章

修改若依框架Token的过期时间问题

《修改若依框架Token的过期时间问题》本文介绍了如何修改若依框架中Token的过期时间,通过修改`application.yml`文件中的配置来实现,默认单位为分钟,希望此经验对大家有所帮助,也欢迎... 目录修改若依框架Token的过期时间修改Token的过期时间关闭Token的过期时js间总结修改若依

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

四种简单方法 轻松进入电脑主板 BIOS 或 UEFI 固件设置

《四种简单方法轻松进入电脑主板BIOS或UEFI固件设置》设置BIOS/UEFI是计算机维护和管理中的一项重要任务,它允许用户配置计算机的启动选项、硬件设置和其他关键参数,该怎么进入呢?下面... 随着计算机技术的发展,大多数主流 PC 和笔记本已经从传统 BIOS 转向了 UEFI 固件。很多时候,我们也

MySQL中my.ini文件的基础配置和优化配置方式

《MySQL中my.ini文件的基础配置和优化配置方式》文章讨论了数据库异步同步的优化思路,包括三个主要方面:幂等性、时序和延迟,作者还分享了MySQL配置文件的优化经验,并鼓励读者提供支持... 目录mysql my.ini文件的配置和优化配置优化思路MySQL配置文件优化总结MySQL my.ini文件

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

poj 3181 网络流,建图。

题意: 农夫约翰为他的牛准备了F种食物和D种饮料。 每头牛都有各自喜欢的食物和饮料,而每种食物和饮料都只能分配给一头牛。 问最多能有多少头牛可以同时得到喜欢的食物和饮料。 解析: 由于要同时得到喜欢的食物和饮料,所以网络流建图的时候要把牛拆点了。 如下建图: s -> 食物 -> 牛1 -> 牛2 -> 饮料 -> t 所以分配一下点: s  =  0, 牛1= 1~