BIOS/UEFI基础——UEFI网络框架之UNDI

来源:互联网 发布:免费英语口语软件 编辑:程序博客网 时间:2024/05/18 00:40

UNDI

UNDI全称Universal Network Driver Interface。

它并不是UEFI网络框架的一部分,甚至也可以不是UEFI的一部分。

不过目前UEFI下的网络驱动都会实现UNDI,这样UEFI就可以通过SNP来调用网卡的底层驱动。

在《UEFI Spec 2_6.pdf》中有对UNDI的详细介绍,这里简单说明下UNDI。


UNDI说到底是定义了一系列的接口,然后SNP来访问这些接口。

SNP如何获取到这些接口呢?

这需要实现了UNDI的网络设备驱动中安装一个NetworkInterfaceIdentifier(NII)协议,目前它的版本是3_10,SNP就可以通过对应的GUID来访问到它:

  //  // Get the NII interface.  //  Status = gBS->OpenProtocol (                  Controller,                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,                  (VOID **) &Nii,                  This->DriverBindingHandle,                  Controller,                  EFI_OPEN_PROTOCOL_BY_DRIVER                  );  if (EFI_ERROR (Status)) {    gBS->CloseProtocol (          Controller,          &gEfiDevicePathProtocolGuid,          This->DriverBindingHandle,          Controller          );    return Status;  }  DEBUG ((EFI_D_INFO, "Start(): UNDI3.1 found\n"));  Pxe = (PXE_UNDI *) (UINTN) (Nii->Id);
如上面代码所示,NII中最重要的是PXE_UNDI指针。

它的结构体如下:

typedef union u_pxe_undi {  PXE_HW_UNDI hw;  PXE_SW_UNDI sw;} PXE_UNDI;
可以看到它存在两种类型,从字面意思上看一种是硬件的,一种是软件的,对应的结构体如下:


这种结构体有一个奇怪的名字叫!PXE,不知道这里的叹号表示什么意思,难道是表示“非”。

上面的结构体成员不一一介绍了,可以参考《UEFI Spec 2_6.pdf》或者其它版本也可以。

从这里我们可以看出硬件UNDI和软件UNDI的一个重大区别,即硬件UNDI通过往MMIO或者IO寄存器写命令来调用底层接口,而软件UNDI通过网络设备驱动提供出来的Entry Point来调用底层接口。

从目前SNP的实现来看,硬件UNDI并不支持。

  if ((Pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) != 0) {    Snp->IsSwUndi             = FALSE;    Snp->IssueUndi32Command   = &IssueHwUndiCommand;  } else {    Snp->IsSwUndi = TRUE;    if ((Pxe->sw.Implementation & PXE_ROMID_IMP_SW_VIRT_ADDR) != 0) {      Snp->IssueUndi32Command = (ISSUE_UNDI32_COMMAND) (UINTN) Pxe->sw.EntryPoint;    } else {      Snp->IssueUndi32Command = (ISSUE_UNDI32_COMMAND) (UINTN) ((UINT8) (UINTN) Pxe + Pxe->sw.EntryPoint);    }  }
上面的代码是用来获取访问网络驱动底层实现的接口,可以看到对于硬件UNDI直接使用了IssueHwUndiCommand()这个函数,但是它直接返回了Unsupported。

而软件UNDI的接口是从!PXE这个结构体中获取的。

对于底层接口的访问如下图所示:

从软件来看,实际上就是下面的几个步骤:

1. 填充CDB;

2. 调用Snp->IssueUndi32Command,参数就是CDB;

3. 判断返回值;

已SNP中的PxeStart()函数为例:

/**  Call UNDI to start the interface and changes the snp state.  @param  Snp                    pointer to snp driver structure.  @retval EFI_SUCCESS            UNDI is started successfully.  @retval EFI_DEVICE_ERROR       UNDI could not be started.  **/EFI_STATUSPxeStart (  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;  }  //  // Issue UNDI command and check result.  //  DEBUG ((EFI_D_NET, "\nsnp->undi.start()  "));  (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);  if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {    //    // UNDI could not be started. Return UNDI error.    //    DEBUG (      (EFI_D_ERROR,      "\nsnp->undi.start()  %xh:%xh\n",      Snp->Cdb.StatCode,      Snp->Cdb.StatFlags)      );    return EFI_DEVICE_ERROR;  }  //  // Set simple network state to Started and return success.  //  Snp->Mode.State = EfiSimpleNetworkStarted;  return EFI_SUCCESS;}
CBD结构体如下:


OpCode是操作码,不同的操作对OpFlags、CPB结构体、DB结构体(就是CPBxxx,DBxxx那几个成员,它们对应到结构体中)都会有影响;

StatCode和StatFlags是返回的参数,也受到OpCode的影响;

IFnum用来处理一个NII对应多个物理网络设备的情况,值从0开始,算是一个Index;

Control可以指示使用了一个CDB还是多个,还可以指示当操作忙时是等待命令执行还是直接返回失败;

OpCode的值可以在UefiPxe.h中找到具体的值。

Snp中的所有操作,实际上都到最后都是使用上述的方式来完成的。


0 0
原创粉丝点击