第1章:UEFI/BIOS调试:进入UEFI Shell、查看内存映射、修改启动参数、调试启动阶段的卡死问题

大家好,我是老张。做AMD嵌入式开发十几年,最头疼的莫过于板子刚上电就卡死。屏幕黑着,串口没输出,你都不知道该从哪里下手。今天咱们就聊聊UEFI/BIOS调试的那些事儿,特别是启动阶段卡死的问题。

说实话,UEFI Shell是我用得最多的调试工具。它就像Windows里的CMD,但功能更底层。你想想看,在操作系统还没起来的时候,UEFI Shell就能帮你查看硬件状态,这多方便。

1.1 进入UEFI Shell的几种方式

我习惯把UEFI Shell的进入方式分成三类。每种都有它的适用场景,咱们一个一个说。

1.1.1 从BIOS设置界面进入

这是最常用的方法。板子启动时按Del或F2进BIOS,找到"Boot"或"启动"菜单,里面有个"Launch UEFI Shell"选项。嗯,这里要注意,不同厂商的BIOS菜单名称可能不一样。我在项目中遇到过,某款AMD平台的BIOS把它藏在了"Advanced"菜单下,找了半天。

小技巧: 如果找不到"Launch UEFI Shell",试试在BIOS里搜索"Shell"关键字。现在很多BIOS都支持搜索功能。

1.1.2 从启动设备加载

把UEFI Shell的EFI文件放到U盘里,插到板子上,从U盘启动。具体做法:

  1. 准备一个FAT32格式的U盘
  2. 创建目录结构:EFI\BOOT\
  3. 把Shell.efi重命名为BOOTx64.efi放进去
  4. 插到板子上,从U盘启动

我个人习惯用这种方法。因为有时候BIOS本身就有问题,根本进不去设置界面。这时候U盘启动就是救命稻草。

1.1.3 串口重定向进入

这个稍微高级点。有些AMD嵌入式板卡支持串口重定向,你可以在BIOS里开启"Serial Console"或"COM Port Redirection"。然后通过串口终端(比如PuTTY)就能看到UEFI Shell的界面。

注意: 串口重定向的波特率通常是115200,但有些板子默认是9600。如果连不上,先检查波特率设置。

1.2 查看内存映射

进了UEFI Shell,第一件事就是看内存。用memmap命令就能搞定。输出大概长这样:

Shell> memmap
Type           Start            End              #Pages          Attributes
Available      0000000000000000-000000000009F000 000000000000009F 000000000000000F
Reserved       000000000009F000-00000000000A0000 0000000000000001 000000000000000F
ACPI Reclaim   00000000000A0000-00000000000B0000 0000000000000010 000000000000000F
ACPI NVS       00000000000B0000-00000000000C0000 0000000000000010 000000000000000F
Available      0000000000100000-000000007FFFFFFF 0000000000007FFF 000000000000000F
...

这里我重点看几个东西:

  • Available:可用内存,看起始地址和大小对不对
  • Reserved:保留内存,是不是占用了不该占的地方
  • ACPI Reclaim:ACPI表占用的内存,这个后面调试电源管理时有用

有一次,客户说板子内存只有1GB,但硬件明明焊了2GB。我进UEFI Shell一看,memmap显示可用内存只有1GB。再仔细查,发现BIOS里把高位内存映射到了PCIe设备上。说白了,就是地址冲突了。

1.3 修改启动参数

启动参数这东西,说白了就是告诉操作系统该怎么启动。在UEFI Shell里,我们可以用setvar命令来修改。

比如,你想让Linux内核输出更多调试信息:

Shell> setvar -guid 8be4df61-93ca-11d2-aa0d-00e098032b8c -name "BootArgs" -attr BS+RT "console=ttyS0,115200 earlyprintk debug"

这个GUID是UEFI规范里定义的全局变量GUID。说实话,记不住也没关系,我都是直接复制粘贴的。

重要: 修改启动参数前,最好先用dmpstore命令备份一下当前的所有UEFI变量。万一改坏了,还能恢复。

1.4 调试启动阶段的卡死问题

这才是今天的重头戏。板子卡死,我一般按这个顺序排查:

1.4.1 先看POST码

大多数AMD嵌入式板卡都有POST码指示灯。如果板子卡在某个POST码上,查一下对应厂商的POST码表,基本就能定位到是哪个模块出了问题。

我记得有一次,板子卡在POST码"0x58"上。查表发现是内存初始化失败。结果发现是内存条没插紧。嗯,这种低级错误其实挺常见的。

1.4.2 用UEFI Shell的调试命令

如果POST码没有,或者POST码正常但系统还是卡死,那就进UEFI Shell。用pci命令看看PCIe设备枚举情况:

Shell> pci
Seg  Bus  Dev  Func  Vendor  Device  Class
000  000  000  000   1022    1450    0600
000  000  000  001   1022    1452    0600
000  000  002  000   1022    1453    0604
...

重点关注那些Vendor ID是"FFFF"的设备。这说明设备没被正确识别,大概率是硬件问题。

1.4.3 检查启动设备

卡死最常见的原因就是启动设备有问题。用map -r命令看看所有可用的块设备:

Shell> map -r
Mapping table
      BLK0: Alias(s):HD1a0a1a0:;BLK0:
          PciRoot(0x0)/Pci(0x1,0x1)/Sata(0x0,0x0,0x0)
      BLK1: Alias(s):HD2a0a1a0:;BLK1:
          PciRoot(0x0)/Pci(0x1,0x1)/Sata(0x1,0x0,0x0)
      BLK2: Alias(s):HD3a0a1a0:;BLK2:
          PciRoot(0x0)/Pci(0x1,0x1)/Usb(0x0,0x0)

如果启动设备没出现,那就是硬件没识别到。如果出现了但启动还是卡死,那就用load命令手动加载启动管理器,看看报什么错。

经验之谈: 我遇到过好几次,板子卡死是因为UEFI驱动加载顺序不对。这时候可以试试在BIOS里关闭"Fast Boot"或"Quick Boot"选项,让BIOS慢慢初始化所有设备。

1.5 实战案例:一次典型的启动卡死调试

去年有个项目,板子启动到UEFI阶段就卡死了。POST码停在"0x92",查表说是"PCIe设备枚举"阶段。我进UEFI Shell,用pci命令一看,发现某个PCIe设备的Vendor ID是"FFFF"。

再查硬件原理图,发现这个设备是板载的NVMe SSD。量了一下供电,发现3.3V电源正常,但1.8V的参考电压只有1.2V。原来是电源芯片虚焊了。重新焊接后,板子正常启动。

你看,有时候问题就是这么简单。但如果没有UEFI Shell,你可能还在那里瞎猜。

1.6 总结

UEFI/BIOS调试,说白了就是三板斧:进Shell、看内存、查设备。掌握了这些,大部分启动卡死问题都能搞定。当然,有些问题确实比较隐蔽,比如时序问题、信号完整性问题,那就需要示波器了。但那是另一个话题。

下一章,咱们聊聊AMD处理器的内存调试。内存这东西,坑更多。到时候我给大家讲讲我踩过的那些坑。

课后作业: 找一块AMD嵌入式板卡,尝试用三种不同的方式进入UEFI Shell。然后用memmappci命令查看系统状态。记录下你看到的输出,对比一下和理论值有什么不同。

公众号:蓝海资料掘金营,微信deep3321