2026年2月更新,Step to UEFI 文章索引:
Ch398:USB千兆网络方案的新选择
作为BIOS工程师,单纯的 BIOS问题不多,更多的时间是在处理各种兼容性问题。BIOS最开始的目标就是用于统一硬件接口,以便软件和操作系统控制硬件。自然而然,客户遇到问题时首先会想到找BIOS工程师。
前一段时间,客户提出一个关于USB 千兆网卡的性能问题,为此我特地研究了网卡传输性能评估方法。正好最近听说WCH 推出了一款USB千兆网卡芯片:Ch398,于是申请样品进行简单的测试。这款产品和 Realtek 的 RTL8153 是Pin2Pin兼容的。
根据官网介绍,这款方案的特点如下:
- 单芯片USB3.0转10M/100M/1000M以太网,集成USB PHY和以太网PHY
- 支持CDC-NCM协议和CDC-ECM协议及RNDIS协议,免安装驱动程序或可选厂商驱动程序
- 支持10Mbps、100Mbps和1000Mbps速率,兼容IEEE 802.3、IEEE 802.3u和IEEE 802.3ab
- 支持10BASE-T、100BASE-TX、1000BASE-T及自动协商
- 支持1000BASE-T标准CAT5E、CAT6双绞线超百米传输距离
- 支持IEEE 802.3az节能协议
- 支持USB3.2 Gen1,向前兼容USB3.1、USB3.0、USB2.1、USB2.0、USB1.1协议规范
- 支持LPM(Link Power Management),支持符合USB3.2 Gen1协议规范的U1/U2/U3以及符合USB2.1协议规范的L1/L2电源管理模式
- 内置TX/RX封包缓冲
- 支持IPv4/IPv6封包校验,支持IPv4 TCP/UDP/HEAD和IPv6 TCP/UDP封包校验生成和检查
- 支持IPV4/IPV6的TCP大包分片
- 支持IEEE 802.3x流量控制和半双工冲突压力回退流量控制
- 支持IEEE 802.3Q VLAN标记
- 支持巨型帧传输
- 支持休眠模式和低功耗的睡眠模式,支持网络低功耗配置,支持动态电源管理
- 支持通过魔术包和网络唤醒包等事件进行远程唤醒
- 内置以太网收发器支持Auto-MDIX自动交换TX/RX,自动识别正负信号线
- 支持LED闪烁频率和占空比配置
- 内置LDO调压器和DC-DC降压器,支持单一5V或者单一3.3V供电,外围精简
- 支持25MHz无源晶振或外置时钟输入
- 工业级温度范围:-40~85℃
- 提供QFN40封装形式
和官方申请了一块Ch398开发板,上面的芯片远比我想象中要小,外围零件也非常少。

拿到手之后插入电脑即可使用,Win11已经内置了驱动无需额外安装,这点有点让我出乎预料。
接下来对这个网卡的传输性能通过简单的试验进行评估,测试环境是单网线双机互联,测试使用的是 Intel 最新一代平台 Panther Lake Ultra 325)。
1.共享目录文件拷贝测试
一端设置了共享目录,其中使用工具生成了一个 100G 的文件从另外的机器进行访问。这是最简单的,不用第三方工具即可进行的测速方法。
下面是测试的视频可以看到传输速度能够始终维持在 110MB/S以上,同时芯片摸起来并不是特别烫。
下面是一个时长16分钟的视频,展示了完整的传输过程:
比较有意思的是,使用共享文件测试的网速会受到系统硬盘性能的影响,比如,视频后期可以从Task Manager中看到,因为硬盘占用率出现波动,传输速度也受到了影响。我猜测这是因为SSD的缓冲耗尽,所以前期硬盘工作正常,传输后期出现写入性能的下降。如果你在工作中遇到这样的问题,不妨考虑一下硬盘的因素。此外,传输速度的稳定性还可能受到 Power Mode 策略和CPU性能的影响。
2.第三方网络测试工具
使用 iperf3(官网 https://iperf.fr/)
第一步,在服务器端运行 iperf3.exe -s

服务器端工作的截图
第二步,在被测机端运行 iperf3.exe -c 192.168.0.4 -b -1000m -u

被测试机器端运行后的截图
完整测试的工作视频如下:
可以看到,这种测试方法传输速度上比前面的共享文件会快一些,同时因为不依赖硬盘性能会使得测试更加单纯,同时这款软件能够生成简单的报告方便进一步评估。
作为对比,我同样测试了一下RTL8153性能,在Windows 文件共享拷贝环节,偶然会发生速度掉到900Mbps 下的情况,整体来看传输速度似乎对硬盘的性能更加依赖。
同样的使用 iperf 测试, RTL8153性能能够达到 Ch398 水平。
这个测试更加证明了Windows 共享拷贝文件测试网速,会受到网速之外的其他因素。如果有条件,尽量使用 iperf 进行测试。
这里可以看到 Ch398作为国产USB千兆网络方案,性能完全可以满足传输需求。此外,国产芯片更容易得到技术支持,双方在沟通上不会存在障碍,这也是选择国产芯片的充足理由。
如果在设计上有USB扩展千兆网络的需求,不妨考虑Ch398这款国产芯片。
Windows 下的命令行 PCI 查询工具
推荐一个 PDF 处理工具
很多时候,我们需要对 PDF 文件进行一些简单的处理,诸如:拆分合并加水印等等。通常情况下,在线的 PDF 工具已经足够应付。但是如果考虑到隐私保密和速度的问题,本地工具会更合适一些。
最近我有一个将扫描后的 PDF 合并的需求,最后使用了PDFtk 这个工具,网站是 https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/。
有兴趣的朋友可以试试。

BCDEDIT 创建一个 WinDBG 选项
本文介绍通过 BCDEDIT 创建一个 Windows 启动选项,这个选项中打开 WinDBG 功能。
根据当前已有启动项拷贝出来一个名为 “WinDBG”的启动项, 这个命令运行之后会生成一个 GUID,需要记下来
bcdedit /copy {current} /d "windbg"
设置启动超时 45秒
bcdedit /timeout 45
设置打开 Debug 功能
bcdedit /debug {前面的 GUID} on
设置使用 USB Debug
bcedit /dbgsettings usb targetname:labz
指定 XHCI
bcdedit /set "{dbgssettings}" busparams 0.20.0

这样,每次启动的时候都会出现一个菜单,选择第一项会正常启动Windows,选择第二项即可启动能够通过 WinDBG 调试的 Windows

TIPS:一种C语言奇怪的数组引用方式
最近看到了一种奇怪的写法,例如下面的代码中:Print(L”%d “,i[segM]); 这个语句,实际上是Print(L”%d “,segM[i]) 的意思。
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
UINT8 segM[]={
0xAA,0xBB,0xCC,0xDD,0xEE,0xFF
};
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
)
{
;
for (UINT8 i=0;i<6;i++) {
Print(L"%d ",i[segM]);
}
return(0);
}
解释说数组访问操作符 [] 是对称的,即:
a[b] == b[a]
当然了,我个人是非常不建议你在代码中使用上面的方法的。
好用的C语言分析工具 CTags
CTags 是一款开源的 C语言分析工具,项目地址是:
https://github.com/universal-ctags/ctags
使用这个工具可以分析C语言,例如:
ctags C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c
可以得到下面的结果;
!_TAG_EXTRA_DESCRIPTION anonymous /Include tags for non-named objects like lambda/
!_TAG_EXTRA_DESCRIPTION fileScope /Include tags of file scope/
!_TAG_EXTRA_DESCRIPTION pseudo /Include pseudo tags/
!_TAG_EXTRA_DESCRIPTION subparser /Include tags generated by subparsers/
!_TAG_FIELD_DESCRIPTION epoch /the last modified time of the input file (only for F\/file kind tag)/
!_TAG_FIELD_DESCRIPTION file /File-restricted scoping/
!_TAG_FIELD_DESCRIPTION input /input file/
!_TAG_FIELD_DESCRIPTION name /tag name/
!_TAG_FIELD_DESCRIPTION pattern /pattern/
!_TAG_FIELD_DESCRIPTION typeref /Type and name of a variable or typedef/
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_KIND_DESCRIPTION!C d,macro /macro definitions/
!_TAG_KIND_DESCRIPTION!C e,enumerator /enumerators (values inside an enumeration)/
!_TAG_KIND_DESCRIPTION!C f,function /function definitions/
!_TAG_KIND_DESCRIPTION!C g,enum /enumeration names/
!_TAG_KIND_DESCRIPTION!C h,header /included header files/
!_TAG_KIND_DESCRIPTION!C m,member /struct, and union members/
!_TAG_KIND_DESCRIPTION!C s,struct /structure names/
!_TAG_KIND_DESCRIPTION!C t,typedef /typedefs/
!_TAG_KIND_DESCRIPTION!C u,union /union names/
!_TAG_KIND_DESCRIPTION!C v,variable /variable definitions/
!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
!_TAG_OUTPUT_FILESEP slash /slash or backslash/
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
!_TAG_OUTPUT_VERSION 1.1 /current.age/
!_TAG_PARSER_VERSION!C 2.2 /current.age/
!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
!_TAG_PROC_CWD C:/Users/yanbwang/Downloads/ctags/ //
!_TAG_PROGRAM_AUTHOR Universal Ctags Team //
!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/
!_TAG_PROGRAM_URL https://ctags.io/ /official site/
!_TAG_PROGRAM_VERSION 6.2.0 /p6.2.20260125.0/
!_TAG_ROLE_DESCRIPTION!C!function foreigncall /called in foreign languages/
!_TAG_ROLE_DESCRIPTION!C!function foreigndecl /declared in foreign languages/
!_TAG_ROLE_DESCRIPTION!C!header local /local header/
!_TAG_ROLE_DESCRIPTION!C!header system /system header/
!_TAG_ROLE_DESCRIPTION!C!macro undef /undefined/
!_TAG_ROLE_DESCRIPTION!C!struct foreigndecl /declared in foreign languages/
AddressWidthInitialization C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^AddressWidthInitialization ($/;" f typeref:typename:VOID
GetFirstNonAddress C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^GetFirstNonAddress ($/;" f typeref:typename:STATIC UINT64
GetPeiMemoryCap C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^GetPeiMemoryCap ($/;" f typeref:typename:STATIC UINT32
GetSystemMemorySizeAbove4gb C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^GetSystemMemorySizeAbove4gb ($/;" f typeref:typename:STATIC UINT64
GetSystemMemorySizeBelow4gb C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^GetSystemMemorySizeBelow4gb ($/;" f typeref:typename:UINT32
InitializeRamRegions C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^InitializeRamRegions ($/;" f typeref:typename:VOID
PublishPeiMemory C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^PublishPeiMemory ($/;" f typeref:typename:EFI_STATUS
Q35TsegMbytesInitialization C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^Q35TsegMbytesInitialization ($/;" f typeref:typename:VOID
QemuInitializeRam C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^QemuInitializeRam ($/;" f typeref:typename:STATIC VOID
mPhysMemAddressWidth C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^UINT8 mPhysMemAddressWidth;$/;" v typeref:typename:UINT8
mQ35SmramAtDefaultSmbase C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^BOOLEAN mQ35SmramAtDefaultSmbase = FALSE;$/;" v typeref:typename:BOOLEAN
mQ35TsegMbytes C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^STATIC UINT16 mQ35TsegMbytes;$/;" v typeref:typename:STATIC UINT16
mS3AcpiReservedMemoryBase C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^STATIC UINT32 mS3AcpiReservedMemoryBase;$/;" v typeref:typename:STATIC UINT32
mS3AcpiReservedMemorySize C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^STATIC UINT32 mS3AcpiReservedMemorySize;$/;" v typeref:typename:STATIC UINT32
类似的,可以直接分析取得C语言中的函数名,命令是:
ctags --c-kinds=f C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c
输出结果:
AddressWidthInitialization C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^AddressWidthInitialization ($/;" f typeref:typename:VOID
GetFirstNonAddress C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^GetFirstNonAddress ($/;" f typeref:typename:STATIC UINT64
GetPeiMemoryCap C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^GetPeiMemoryCap ($/;" f typeref:typename:STATIC UINT32
GetSystemMemorySizeAbove4gb C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^GetSystemMemorySizeAbove4gb ($/;" f typeref:typename:STATIC UINT64
GetSystemMemorySizeBelow4gb C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^GetSystemMemorySizeBelow4gb ($/;" f typeref:typename:UINT32
InitializeRamRegions C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^InitializeRamRegions ($/;" f typeref:typename:VOID
PublishPeiMemory C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^PublishPeiMemory ($/;" f typeref:typename:EFI_STATUS
Q35TsegMbytesInitialization C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^Q35TsegMbytesInitialization ($/;" f typeref:typename:VOID
QemuInitializeRam C:/BuildBs/edk2202302/edk2/OvmfPkg/Bhyve/PlatformPei/MemDetect.c /^QemuInitializeRam ($/;" f typeref:typename:STATIC VOID
Step to UEFI (302)VC 中的机器码填充
VC 的 X86 模式下无法直接使用 _ASM 关键字定义汇编代码,最近偶然看到了VC 支持Intrinsics(内联函数),它是编译器提供的特殊函数,它们直接映射到特定的处理器指令,让你能够在 C++ 代码中使用底层硬件功能。
于是,编写代码测试:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
)
{
UINT32 cpuInfo[4];
// CPUID 指令
__cpuid(cpuInfo, 1);
//NOP 指令
__nop();
return(0);
}
使用 /FACS 查看生成的汇编代码,对应片段如下:
ShellAppMain PROC ; COMDAT
; 19 : {
$LN3:
00000 48 89 54 24 10 mov QWORD PTR [rsp+16], rdx
00005 48 89 4c 24 08 mov QWORD PTR [rsp+8], rcx
0000a 53 push rbx
0000b 48 83 ec 10 sub rsp, 16
; 20 : UINT32 cpuInfo[4];
; 21 :
; 22 : // CPUID 指令
; 23 : __cpuid(cpuInfo, 1);
0000f b8 01 00 00 00 mov eax, 1
00014 33 c9 xor ecx, ecx
00016 0f a2 cpuid
00018 4c 8d 04 24 lea r8, QWORD PTR cpuInfo$[rsp]
0001c 41 89 00 mov DWORD PTR [r8], eax
0001f 41 89 58 04 mov DWORD PTR [r8+4], ebx
00023 41 89 48 08 mov DWORD PTR [r8+8], ecx
00027 41 89 50 0c mov DWORD PTR [r8+12], edx
; 24 :
; 25 : __nop();
0002b 90 npad 1
; 26 :
; 27 : return(0);
0002c 33 c0 xor eax, eax
; 28 : }
0002e 48 83 c4 10 add rsp, 16
00032 5b pop rbx
00033 c3 ret 0
ShellAppMain ENDP
可以看到编译后生成了对应的汇编代码(也就是机器码)。特别是 _NOP() 函数,直接生成了0x90 这样的机器码。使用这样的思路可以在代码中使用 _NOP()函数进行填充,然后在编译完成后使用工具替换这部分机器码为我们自定义的代码。让我们的程序更加灵活,实现更多的功能。
===========================================================
2026年3月24日 补充,还可以使用参数作为特征码,例如,代码定义如下:
__rdtscp(0x12345678);
对应生成的机器码如下:
; 26 :
; 27 : __rdtscp(0x12345678);
0002c 0f 01 f9 rdtscp
0002f bb 78 56 34 12 mov ebx, 305419896 ; 12345678H
00034 89 0b mov DWORD PTR [rbx], ecx
那么可以通过搜索 0x12345678 来确定你所需要修改的机器码在二进制文件中的位置。
记录一个ESP32 S3/P4 上奇怪的问题
最近我在使用 ESP32 P4 时遇到了一个奇怪的问题,经过简化,代码如下:
class Ch346 {
public:
Ch346() {};
uint8_t WriteData() {
for (int i = 0; i < 512; i++) {
Serial.println(i);
}
}
};
Ch346 MyCh346;
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("Start");
}
void loop() {
if (Serial.available()) {
char c = Serial.read();
if (c == '1') {
Serial.println("Input 1");
MyCh346.WriteData();
Serial.println("Input 1 Complete");
}
}
}
运行之后,打开串口输入1 然后应该得到 0-511的值,上述代码在Leonardo 板子上上没问题,但是不知道为什么在 ESP32 S3 P4上会一直输出:

===========================================================
有兴趣的朋友可以先思考一下,具体解析在下面:
CH397 UEFI 网络功能测试
CH397是 WCH 推出的USB转LAN芯片,WCH提供了对应的 UEFI驱动,用户有机会在 UEFI 环境中直接使用这个网卡。本次试验是在UEFI Shell 下启动一个 HTTP服务器,然后通过网络内另外一台机器的浏览器对这台机器进行访问。
这次我们使用一台机器作为 DHCP 服务器,能够给被测机器提供IP地址,具体的配置请看之前的文章。
第一步,Boot到UEFI 下,然后 load WCHUSBNIC.EFI 加载驱动

正确加载后,使用 ifconfig -l 列出当前系统中的网卡
第二步,运行如下命令让网卡通过 DHCP分配地址(如果你接入的网络没有DHCP,那么就需要手工指定网卡使用 IP)
Ifconfig -s eth0 dhcp
这步之后,再次运行 ifconfig -l 可以看到网卡的 MAC 还有分配到的 IP地址


第三步,我们在UEFI Shell 下运行一个 webserver

第四步,在主机端浏览器上使用 上面分配到的IP地址即可访问被测机。
具体的工作测试视频,可以在这里看到:
WCHUSBNIC.EFI 下载
TPS63802 升降压芯片模块测试
TPS63802DLAR 是 TI 推出降压/升压转换器。
• 输入电压范围:1.3V 至 5.5V – 器件启动时输入电压大于 1.8V
• 输出电压范围:1.8V 至 5.2V(可调节),计算公式 Vout=(R1/R2+1)*0.5V
• VI ≥ 2.3V、VO = 3.3V 时,输出电流为 2A
这里使用这个芯片制作一个模块,对于外部输入的 1.8-5.5V 的电压转化为 3.3V。


焊接之后如下

接下来对模块进行测试:
1.7V输入时,模块不工作

1.8V时开始工作,输出3.3V

2.4V时输出3.3V

3.4V时输出3.3V

4.4V时输出3.3V

5.4V时输出3.3V

可见,这个芯片非常适合配合 3.7V 充电电池使用。
模块的电路图和PCB下载(立创标准版)
