您的位置:首页 > 理论基础 > 计算机网络

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

2016-11-11 20:46 3627 查看

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_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;
}
//
// 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中的所有操作,实际上都到最后都是使用上述的方式来完成的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  uefi 网络