基础说明
UEFI中的网络框架大致如下:
红框部分是实现UEFI的EDK2开源项目中网络框架自带的实现,红框之外的部分需要网卡设备商提供驱动。UEFI下通常推荐使用最右边的形式,即网卡设备商提供实现了UNDI的网卡驱动。因此UEFI网络框架的另一个形式如下:
其中最底下UNDI部分并不在开源的EDK代码中,它代表的是网卡驱动,所以一般由网卡开发商来实现并提供出来供EDK代码集成。比如Intel的I350网卡驱动可以在Intel® 乙太網路介面卡 完整的驱动程序包下载到:
这里的zip压缩包包含了所有的Intel网卡驱动,其中有一部分就是在UEFI下使用的,如下所示:
这里的几个压缩包都是UEFI下的Intel网卡驱动源码,其中GigUndiDxe.zip可以用于初始化I350等网卡的,其它则是给更高速率的网卡使用的。
UNDI驱动提供的接口可以与SNP对接。而SNP及以上的部分在EDK中都有源码,它们是UEFI下的网络协议栈。另外关于安全部分(TLS或者HTTPS中的S)也可以认为是UEFI网络协议栈的一部分,虽然并不都在EDK开源代码中,但是也能够在其它地方找到对应的源码。
下面是一般的网络模型与UEFI网络模型的简单对比:
其实UEFI网络模型的大部分内容都与普通的TCP/IP网络模型一致,只不过多了一些UEFI特有的驱动,主要是MNP和SNP,而UNDI只是网卡在UEFI下的驱动而已。
代码说明
EDK代码中,可以在NetworkPkg目录下找到大部分的网络协议栈代码,以下是位于\NetworkPkg\NetworkComponents.dsc.inc(对应的EDK开源代码版本edk2-stable202211,不同的版本内容可能稍有差异)中描述的各个网络驱动:
!if $(NETWORK_ENABLE) == TRUENetworkPkg/DpcDxe/DpcDxe.inf!if $(NETWORK_SNP_ENABLE) == TRUENetworkPkg/SnpDxe/SnpDxe.inf!endif!if $(NETWORK_VLAN_ENABLE) == TRUENetworkPkg/VlanConfigDxe/VlanConfigDxe.inf!endifNetworkPkg/MnpDxe/MnpDxe.inf!if $(NETWORK_IP4_ENABLE) == TRUENetworkPkg/ArpDxe/ArpDxe.infNetworkPkg/Dhcp4Dxe/Dhcp4Dxe.infNetworkPkg/Ip4Dxe/Ip4Dxe.infNetworkPkg/Udp4Dxe/Udp4Dxe.infNetworkPkg/Mtftp4Dxe/Mtftp4Dxe.inf!endif!if $(NETWORK_IP6_ENABLE) == TRUENetworkPkg/Dhcp6Dxe/Dhcp6Dxe.infNetworkPkg/Ip6Dxe/Ip6Dxe.infNetworkPkg/Udp6Dxe/Udp6Dxe.infNetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf!endifNetworkPkg/TcpDxe/TcpDxe.infNetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf!if $(NETWORK_TLS_ENABLE) == TRUENetworkPkg/TlsDxe/TlsDxe.infNetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.inf!endif!if ($(NETWORK_HTTP_BOOT_ENABLE) == TRUE) OR ($(NETWORK_HTTP_ENABLE) == TRUE)NetworkPkg/DnsDxe/DnsDxe.infNetworkPkg/HttpDxe/HttpDxe.infNetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf!endif!if $(NETWORK_HTTP_BOOT_ENABLE) == TRUENetworkPkg/HttpBootDxe/HttpBootDxe.inf!endif!if $(NETWORK_ISCSI_ENABLE) == TRUENetworkPkg/IScsiDxe/IScsiDxe.inf!endif
!endif
可以看到EDK支持IPv4和IPv6两个版本,不过目前BIOS下使用比较多的还是IPv4,后面的分析也主要针对IPv4版本。以上的协议,按照UEFI代码模型分类的话大部分都属于UEFI Driver Model。它们都会在DXE阶段安装类似如下的结构:
//
// Simple Network Protocol Driver Global Variables
//
EFI_DRIVER_BINDING_PROTOCOL gSimpleNetworkDriverBinding = {SimpleNetworkDriverSupported,SimpleNetworkDriverStart,SimpleNetworkDriverStop,0xa,NULL,NULL
};
并在BDS阶段执行判断(xxxSupported)和执行具体实现(xxxStart),这种结构称为Driver Binding Protocol。
本文使用开源的EDK代码来具体说明UEFI网络协议栈的实现,可以在GitHub - tianocore/edk2: EDK II或者edk2: https://github.com/tianocore/edk2.git (gitee.com)下载到EDK源码,不过本文使用的测试代码来自edk2-beni: 用于学习和验证UEFI BIOS。 (gitee.com),使用该代码可以直接编译出在QEMU虚拟机上使用的BIOS,并可以使用它来测试UEFI网络代码。
另外,本文主要介绍的是UEFI中的网络协议实现代码,重点关注UEFI,而不是网络实现,所以对于UNDI中的网卡实现逻辑、网络协议的具体格式、网络数据包的具体解析等不会详细介绍。