4、UEFI Shell环境:Shell基础命令、Shell脚本编写、设备路径与映射
UEFI Shell,说白了就是你在固件层面的“命令行”。没有操作系统,没有图形界面,你面对的就是一个Shell>提示符。很多工程师觉得这东西过时了,其实不然。我在调试早期启动阶段的Bug时,Shell往往是唯一能用的工具。你想想看,系统连Windows都没加载,你怎么看内存?怎么改变量?怎么确认设备有没有被识别?
这一章,我们就来把Shell环境彻底搞明白。我会从基础命令讲起,再到脚本编写,最后聊聊设备路径和映射——这部分是很多人的盲区,但恰恰是UEFI调试的核心。
4.1 进入UEFI Shell的几种方式
先说说怎么进去。不同厂商的BIOS入口不太一样,但原理相通。
| 方式 | 操作步骤 | 适用场景 |
|---|---|---|
| BIOS内置Shell | 启动时按F2/Del进入BIOS,找到"Boot to Shell"或类似选项 | 大多数消费级主板 |
| UEFI Shell.efi文件 | 将Shell.efi放在FAT32格式的U盘,从U盘启动 | 定制固件、嵌入式开发 |
| 固件调试串口 | 通过串口重定向进入Shell | 服务器、开发板调试 |
| Boot Manager菜单 | 启动时按F12选择"UEFI Shell" | 部分品牌机 |
我的建议: 手边常备一个FAT32格式的U盘,里面放好Shell.efi和几个常用工具。我在项目现场遇到过BIOS里根本没有Shell入口的情况,全靠这个U盘救命。
4.2 Shell基础命令——够用就行
Shell命令不需要全记住,常用的就那么十几个。我列个清单,你照着练一遍就熟了。
4.2.1 文件与目录操作
Shell> fs0: # 切换到第一个文件系统
Shell> ls # 列出当前目录内容
Shell> ls -l # 详细列表(带大小、属性)
Shell> cd \EFI\BOOT # 切换目录(注意是反斜杠)
Shell> dir # 和ls一样,DOS习惯
Shell> type startup.nsh # 查看文件内容
Shell> edit startup.nsh # 编辑文件(简易文本编辑器)
Shell> cp file1 file2 # 复制文件
Shell> rm file1 # 删除文件
Shell> mkdir mydir # 创建目录
注意: UEFI Shell的路径分隔符是反斜杠\,不是Linux的斜杠/。我刚开始时总搞混,结果命令一直报错。另外,文件名不区分大小写,但建议保持统一风格。
4.2.2 系统信息与诊断
Shell> memmap # 查看内存映射
Shell> drivers # 列出所有加载的驱动
Shell> dh # 显示设备句柄
Shell> devtree # 设备树(非常有用)
Shell> bcfg boot dump # 查看启动项列表
Shell> reset # 重启系统
memmap这个命令我几乎每次调试都会用。它告诉你哪些内存区域是常规内存,哪些是保留的,哪些是ACPI的。有一次客户说系统在某个地址写数据会崩溃,我一查memmap,发现那块区域被标记为MMIO——根本就不是普通内存。
4.2.3 环境变量操作
Shell> set # 列出所有环境变量
Shell> set MyVar HelloWorld # 设置变量
Shell> set MyVar # 查看变量值
Shell> set -d MyVar # 删除变量
环境变量在Shell脚本里特别重要。比如你可以用set Lasterror来检查上一条命令的执行结果。
4.3 Shell脚本编写——自动化你的调试流程
手动敲命令太慢了。遇到重复性工作,写个脚本一劳永逸。
4.3.1 脚本文件格式
Shell脚本就是普通的文本文件,扩展名是.nsh。启动时Shell会自动执行startup.nsh,这个机制很实用。
# 这是一个简单的脚本示例
# 文件名为 test.nsh
echo "=== 系统信息收集脚本 ==="
echo "内存映射:"
memmap
echo ""
echo "设备树:"
devtree
echo ""
echo "启动项:"
bcfg boot dump
echo "脚本执行完毕"
4.3.2 变量与参数
# 使用参数
echo "第一个参数: %1"
echo "第二个参数: %2"
echo "所有参数: %*"
# 变量赋值与使用
set MyPath fs0:\EFI\BOOT
echo "路径是: %MyPath%"
cd %MyPath%
关键点: Shell脚本里变量引用用%变量名%,不是$变量名。这个和DOS批处理一样,和Linux Shell不一样。我见过不少工程师在这里栽跟头。
4.3.3 控制流
Shell脚本支持基本的条件判断和循环,虽然功能有限,但够用。
# if-else 判断
if %1 == "debug" then
echo "进入调试模式"
set DebugMode 1
else
echo "正常模式"
set DebugMode 0
endif
# for 循环
for %a run (1 2 3 4 5)
echo "当前计数: %a"
endfor
# goto 跳转(不推荐,但有时候不得不用)
goto :Start
:End
echo "结束"
goto :EOF
:Start
echo "开始"
goto :End
我的习惯: 写脚本时先画个简单的流程图,再动手写。Shell脚本的调试手段有限,出错了只能靠echo打印信息。我曾经写过一个30行的脚本,因为少写了一个endif,排查了半小时。
4.4 设备路径与映射——理解UEFI的“盘符”逻辑
这部分是很多人的知识盲区。为什么有时候fs0:能访问,有时候不行?为什么U盘插上去不显示?
4.4.1 设备路径是什么
设备路径是UEFI用来唯一标识一个设备的方式。它是一串结构化的数据,描述了设备在系统中的位置。
# 查看设备路径
Shell> dh -d
# 输出示例
# 设备路径: PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)/HD(1,MBR,0x12345678,0x800,0x10000)
这个路径的意思是:从PCI根桥开始,经过PCI设备0x1F功能2,再到SATA端口0,最后是硬盘的第一个分区。每个节点都对应一个硬件层次。
4.4.2 映射——从设备路径到盘符
Shell用fs0:、fs1:这样的映射来简化操作。你可以用map命令查看所有映射。
Shell> map
# 输出示例
# Mapping table
# fs0: :PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)/HD(1,MBR,0x12345678,0x800,0x10000)
# fs1: :PciRoot(0x0)/Pci(0x14,0x0)/Usb(0x0,0x0)/HD(1,MBR,0x87654321,0x800,0x10000)
# blk0: :PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)
# blk1: :PciRoot(0x0)/Pci(0x14,0x0)/Usb(0x0,0x0)
fs代表文件系统(File System),blk代表块设备(Block Device)。只有格式化为FAT12/16/32的设备才能挂载为fs。
避坑指南: 我曾经遇到过一个问题——U盘插上去,map命令能看到blk1:,但就是没有fs1:。查了半天,发现U盘是NTFS格式的。UEFI Shell只认FAT系列文件系统。NTFS、exFAT都不行。解决办法:把U盘重新格式化为FAT32。
4.4.3 手动创建映射
有时候系统没有自动映射,你可以手动创建。
# 手动映射块设备到文件系统
Shell> map -r fs0: blk1:
# 或者用设备路径映射
Shell> map fs2: PciRoot(0x0)/Pci(0x14,0x0)/Usb(0x0,0x0)/HD(1,MBR,0x87654321,0x800,0x10000)
4.4.4 设备路径的典型结构
| 路径节点 | 含义 | 示例 |
|---|---|---|
| PciRoot | PCI根桥 | PciRoot(0x0) |
| Pci | PCI设备 | Pci(0x1F,0x2) |
| Sata | SATA端口 | Sata(0x0,0xFFFF,0x0) |
| Usb | USB设备 | Usb(0x0,0x0) |
| HD | 硬盘分区 | HD(1,MBR,0x12345678,0x800,0x10000) |
| CDROM | 光驱 | CDROM(0x0) |
| MAC | 网卡MAC地址 | MAC(001122334455) |
4.5 实战:用Shell排查启动问题
讲个真实案例。有一次,一台服务器启动后直接进入Shell,不加载操作系统。客户很着急。
我的排查步骤:
- 先看启动项:
bcfg boot dump— 发现启动项列表是空的。 - 检查设备:
map— 能看到fs0:,是系统盘。 - 查看文件:
ls fs0:\EFI\BOOT— 发现bootx64.efi存在。 - 手动启动:
fs0:\EFI\BOOT\bootx64.efi— 系统正常启动了。
问题找到了:启动项丢失,但引导文件还在。修复方法是用bcfg boot add命令把启动项加回去。
# 添加启动项
Shell> bcfg boot add 0 fs0:\EFI\BOOT\bootx64.efi "Windows Boot Manager"
重启后,一切正常。整个过程不到10分钟,全靠Shell。
4.6 本章知识体系
下面这张图帮你理清本章的核心脉络:
总结一下: UEFI Shell不是摆设,它是你排查启动问题的最强工具。基础命令要熟练到肌肉记忆,脚本能帮你自动化重复工作,设备路径和映射是理解UEFI设备模型的关键。这三块掌握了,你就能在固件层游刃有余。