ARM 平台也能改 BIOS 隐藏项了?——gsetupmod 双架构发布
这几年玩固件调试的朋友应该都有一个共同感受:x86 平台想解锁 BIOS 隐藏项、打开被厂商屏蔽的 Advanced 菜单,手段已经非常成熟,工具链也齐全,搜一搜就能找到一大堆教程。但一旦换到 ARM 平台的板子、迷你主机、笔记本或者服务器主控,事情立刻就变得很麻烦。UEFI 规范明明在 ARM 和 x86 上都是同一套框架,实际动起手来却处处碰壁——EFI Shell 下能用的脚本少、变量读写工具不通用、固件模块结构差异又大,很多人试到一半就放弃了。
这篇文章要聊的 gsetupmod,正好补上了这个缺口。它的思路和 x86 上常见的 GRUB Shell 变量修改法有相似之处,但针对 ARM 平台重新设计了模块解析和变量定位逻辑,同时在双架构下发布,x86 和 ARM64 的固件都能覆盖到。适合谁看?如果你手上有 ARM 平台的开发板、工控机、NAS 主控板,或者只是想搞清楚 UEFI Setup 隐藏项到底是怎么被“藏”起来的,那么这篇文章能帮你少走不少弯路。我会从原理开始讲,再给出一套可以照着做的实操流程,最后把容易踩的坑一并整理出来。
1. 为什么 ARM 平台改 BIOS 隐藏项比 x86 难这么多
1.1 传统固件调试工具几乎都是 x86 的“专属玩具”
先看 x86 平台为什么好折腾。UEFI 固件从设计上说,核心调度逻辑(DXE 阶段、BDS 阶段)是由 PI 规范统一的,不管是 Intel 还是 AMD 的平台,只要遵循 PI 规范,固件内部结构就大差不差。于是那些经典工具——UEFITool、IFRExtractor、RU.efi、GRUB Shell 里的 setup_var,全部在这个统一的框架下工作得很好。你会看到很多人用一条 GRUB 命令就能改变量值、解开隐藏菜单,本质上是绕过了 BIOS Setup 页面的图形界面,直接去操作固件中的 UEFI 非易失性变量。
但这个生态有个隐含前提:早期的 UEFI 实践主要围绕 x86 展开。x86 平台从 BIOS 过渡到 UEFI 的时间早,厂商投入大,工具链自然最完善。ARM 平台虽然也跑 UEFI,可真正大规模落地是近几年的事,很多工具根本没有 ARM64 版本,或者即使能编译,跑起来也会因为固件实现差异而失灵。比如 RU.efi 这种经典的变量查看修改工具,在部分 ARM 机器上连加载都困难,更别提去解析 IFR 数据了。
1.2 ARM 平台有自己的“方言”,不是简单换一个 CPU 架构
ARM 平台难改,不仅是因为工具少,更深层的原因是固件实现的碎片化。x86 平台有比较统一的 PC 兼容层,主板厂商在 UEFI 上做差异化,更多是围绕菜单选项、默认策略、板载外设来展开,底层框架差异有限。ARM 平台则不太一样:同样是跑 UEFI,服务器板卡、消费级 ARM 笔记本、嵌入式核心板、网络设备主控板,它们的固件来源各不相同,有的基于 EDK2 官方主干,有的用 Linaro 分支,还有的直接拿芯片厂商 SDK 改,隐藏项的实现方式非常多样。
这就带来一个很现实的问题:你在 x86 上只需要“找到变量名、修改变量值、重启”三步,在 ARM 平台上可能需要先花大量时间搞清楚固件用的是哪一套 HII 配置、隐藏项是被哪个条件变量控制的、变量 GUID 是否被厂商改过。很多人入手 ARM 平台后第一反应是“怎么资料这么少”,其实不是资料少,而是每一家的实现都不一样,很难写出一份通用的教程。gsetupmod 这次双架构发布,最实际的意义是提供了一个能解析常见 ARM UEFI 固件模块、定位并修改隐藏 Setup 项的工具,把碎片化程度降低了一点。
2. 隐藏选项到底是怎么来的,先搞懂 UEFI Setup 的底层逻辑
2.1 你看到的每一个 BIOS 设置项,本质上都是一段 IFR 数据
要做 BIOS 隐藏项修改,第一步不是拿工具去改,而是先弄清楚你改的是什么。UEFI Setup 页面并不是一个简单的“图形界面配置文件”,它由一组 DXE 驱动在运行时动态生成,这些驱动通过 HII(Human Interface Infrastructure)数据库对外提供表单。每个页面上的每一项——不管是下拉框、复选框还是数值输入框,在固件编译时都是由 VFR 源文件描述,再被编译成 IFR 二进制数据。
IFR 的实质是类似脚本的解释执行代码。一个下拉框选项(OneOf)、一个开关选项(CheckBox)、一个数值输入框(Numeric),在 IFR 数据里都有对应的 opcode。HII 数据库在启动阶段读取这些 opcode,按规则来绘制界面,同时把设定值保存到 UEFI 非易失性变量中。也就是说,你在 BIOS 设置页面里看到的每一个选项,背后都有一个变量和它对应。改隐藏项的本质,要么是直接把这个选项从“隐藏”状态改成“显示”状态,要么是绕过界面直接写入对应变量的值。
这里有一个很多教程没讲透的点:隐藏项并不一定需要修改二进制代码才能显示出来。很多隐藏项只是被一个条件变量或一个 PCD(平台配置数据库)开关控制着,只要让条件成立,菜单就自动出现了。gsetupmod 的核心工作,其实就是站在 HII 数据库的角度去“问”固件:你有哪几个页面、哪几个选项、每个选项由哪个变量控制,然后根据你指定的条件去修改这个变量的值。
2.2 厂商为什么要把某些选项藏起来
了解厂商的“藏”法,有助于判断哪些选项值得解锁。不同厂商隐藏选项的原因各不相同,大致能归成三类。第一类是工厂调试项,比如内存电压微调、PLL 配置、总线时序参数,这些在量产阶段用于调校硬件,正常用户不需要碰,打开之后误操作还可能损坏设备,所以厂商默认隐藏。第二类是兼容性选项,比如某些 ARM 主板对 PCIe 链路速度、USB 协议模式做了限定,正常固件版本里不给用户修改入口,但调试时确实需要调整,这类隐藏项解锁之后非常实用。第三类是软开关项,常见的如制造模式、调试日志开关、安全启动策略的额外控制位,它们往往不是为了给用户用,而是为了方便开发维护人员在工厂或售后环境里做诊断。
理解这一点后,你在决定要改哪些选项时就能更有针对性。盲目打开所有隐藏项不是好习惯,有些工厂调试项一旦改了错误的值,轻则系统不稳定,重则设备启动异常。比较务实的做法是:先锁定你要解决的问题,再找到对应的菜单或变量,小范围修改。
2.3 gsetupmod 的工作逻辑:解析、定位、修改
gsetupmod 的工作流程可以拆成几个动作。它会先从固件映像中识别出负责 Setup 页面的 DXE 驱动模块,再从驱动模块中提取 IFR 数据。拿到数据之后,工具会建立一份“隐藏选项清单”,列出每个选项的变量名、变量 GUID、偏移量和可能的取值集合。你用交互界面或者命令行参数选中目标选项后,工具把它转化为实际的 UEFI 变量写入操作,说白了就是直接写到你固件里的某个变量区域。
在 x86 平台上,类似的思路通常用 GRUB Shell 的 setup_var 系列脚本实现,但 GRUB Shell 在 ARM 平台的可用性很不稳定,还需要单独适配。gsetupmod 之所以能做双架构,关键在于它把 IFR 解析和变量写入两个环节完全解耦了——解析环节面向 EDK2 固件共同的数据格式,相对通用;写入环节则根据运行时环境自动切换到对应的架构实现。这算是碰到 ARM 平台碎片化之后比较务实的工程解法,效果我用下来也确实不错。
3. gsetupmod 双架构实操:从提取固件到点亮隐藏菜单
3.1 工具选型的几个选项,为什么我推荐直接试 gsetupmod
在 gsetupmod 出现之前,ARM 平台改 BIOS 隐藏项并不是完全没有办法,只是各有各的痛点。我整理了一下现状,供大家对比参考。
| 方案 | 平台适配 | 上手难度 | 主要问题 |
|---|---|---|---|
| 直接二进制 patch 固件模块 | 理论通用 | 很高 | 需要反汇编能力,不同固件差异大,一个字节偏移算错就变砖 |
| GRUB Shell 变量修改法 | x86 比较稳,ARM 很不稳定 | 中等 | ARM 设备加载 GRUB 麻烦,变量偏移需要手查,效率低 |
| 自己写 EDK2 应用 | 通用,但需要编译环境 | 很高 | 开发周期长,对新手不友好,光搭交叉编译环境就够折腾 |
| gsetupmod | x86 / ARM64 通用性好 | 低到中等 | 项目比较新,资料还在积累中,但基本流程已经可用 |
从我试过的情况来看,如果你是第一次在 ARM 平台上做隐藏项修改,gsetupmod 是最不容易卡住的选择。它不需要你熟悉 ARM 汇编,也不需要你手动反编译固件模块,工具会把 IFR 里的选项直接列出来,你只需要知道你想打开哪个选项就行。而且它对常见 EDK2 固件的解析成功率高,省去了不少前期摸索的时间。
3.2 第一步:提取当前固件,确认架构与模块位置
不管用什么工具,改 BIOS 之前先备份当前固件永远是第一步,这在 ARM 平台尤其重要。部分 ARM 开发板的 SPI Flash 是直接焊在板子上的,一旦刷入错误的固件,恢复比 x86 平台麻烦得多。备份的方式取决于你的设备类型。如果你的板子能在 UEFI Shell 下操作,可以尝试用 Shell 下的固件备份命令直接导出一份完整固件;如果设备使用标准的 UEFI Capsule 更新机制,也可以尝试通过主板厂商提供的刷机工具备份;最直接的办法,是查阅你的板卡用户手册,看有没有官方提供固件备份命令或工具。
拿到固件之后,建议先用 UEFITool 或者类似工具确认固件的基本信息。这里要注意,如果你的固件包是拆包后的单个模块,需要先辨认它属于哪一类模块。负责 Setup 页面的模块通常是 UiApp 或者 Setup 相关的 DXE 驱动,文件名可能包含 Setup、Hii、Menu 之类的字样。在 ARM 平台,这个模块可能会出现在不同于 x86 的 FV(Firmware Volume)分区里,搜索时需要耐心一点。如果你在固件包中看到了 DXE 驱动列表,不妨先把与 Setup、Hii 相关的模块全部导出,备着,后续解析用得上。
这一步其实能过滤掉很多风险。如果固件模块导不出来,说明你的固件可能做了封装或者加密,这种加密固件后续所有修改都可能无效,早点发现比刷坏之后才意识到要好得多。
3.3 第二步:运行 gsetupmod,选择目标模块和变量
环境准备好之后,就可以开始实际操作了。这里以我调试的一块 ARM 开发板为例说明整个过程。
首先是获取固件模块。把上一步用 UEFITool 导出或者从备份工具中提取的 Setup 模块放到工作目录。然后运行 gsetupmod,进入交互模式。工具启动后会扫描指定模块,并把其中包含的所有 IFR 选项以列表形式打印出来。我在实际使用中发现,ARM 固件的模块命名有时候没有 x86 那么直观,建议在列表里先按“变量名”排序,而不是按“选项名”排序,因为有些隐藏项的名称非常笼统,靠变量名定位更快。
定位到目标选项后,下一步是查看它支持的所有取值。这一步非常关键。以常见的 DVMT 预分配显存或 PCIe 速度控制为例,同样一个功能,在不同固件里可能支持不同的值域,比如“Auto / 1G / 2G”和“Auto / 512M / 1G / 2G”就完全不同。确认好取值后,选择设置目标值。gsetupmod 在写入变量前通常会让你二次确认变量名、GUID 和最终值,这一步不要跳过,仔细看一看,避免写错地方。
写入完成后,工具会在当前会话中更新变量,但有些固件环境下,变量写入后并不会立刻反映到当前 Setup 页面,需要重启才会重新加载。也有个别板子的固件实现比较特殊,修改后的变量在启动流程中被固件逻辑再次覆盖,这类我在下一节专门说。
3.4 第三步:重启验证,以及无效时的回退思路
重启进入 BIOS 设置界面后,先对照你刚才修改的选项,确认隐藏菜单是否出现、值是否和设定一致。这里有一个经验:有时候菜单没出现,不一定是写入失败,而是因为隐藏菜单被多个条件同时控制。比如某个 Advanced 子页面,需要在“主开关”和“子开关”两个变量都为特定值时才会显示。你只改了一个,自然看不到效果。
如果修改后无效,我的建议是先别着急重新刷固件。进入 Shell 环境,用 gsetupmod 的查询功能把目标变量的当前值读出来,和写入值做对比。如果变量根本没有被写入,多半是变量 GUID 不匹配,或者固件有运行时过滤器在拦截写入。如果变量写入了但值被复位,多半是固件启动流程中有默认值加载逻辑,比如 PEI 阶段的默认配置覆盖了你的修改。这两种情况处理思路完全不同,前者要重新确认选项所对应的变量定义,后者则需要找更底层的默认值表去改。
我自己遇到过一种情况:某 ARM 网关系列,BIOS 设置里的“串口重定向”选项被隐藏,但无论我怎么改变量,重启后总是恢复默认值。后来排查发现,这个设备的固件在每个启动阶段的早期都会从备份区重新加载一份默认变量,你直接改当前活动变量根本挡不住。这种设备就不能靠 gsetupmod 一改了之,需要分析固件里默认变量表的存储位置,改默认值,才能让修改持久生效。换句话说,工具能把“改哪里”的问题解决掉,但“改完能不能留住”,还得看厂家固件的具体实现,这一点必须心里有数。
4. ARM 平台改 BIOS 最容易踩的坑,以及排查清单
4.1 签名验证与安全启动的先后问题
很多 ARM 设备默认开启 Secure Boot,这在修改隐藏项时会带来一个让人头疼的情况:即使你成功修改了某个变量,重启后固件在验证阶段发现变量区内容与签名记录不一致,可能直接丢弃你的配置,极端情况下还会触发恢复模式。这不是变量写入失败,而是平台的安全策略在起反作用。
遇到这种情况,建议先进入固件设置界面,找到 Secure Boot 相关的启动模式选项,切换成自定义模式或者暂时关闭签名验证。要注意,部分 ARM 板卡在关闭 Secure Boot 后,某些隐藏选项依然受平台信任根保护,需要你把平台密钥(PK)清空后才能正常修改。这个操作有风险,清空前务必确认你手里有原始密钥备份,否则后续安装某些系统可能会卡在验证环节。
4.2 变量存储空间不是无限的
UEFI 非易失性变量存储在 SPI Flash 里,空间是有限的。x86 平台变量空间通常预留了足够余量,但部分 ARM 设备为了削减成本,变量存储区设置得比较小。如果你在一个会话中反复修改多个变量,或者每次修改都触发固件写入大量日志,变量存储空间可能被占满,这时候再尝试写入新变量就会失败,而且失败日志往往不明显,你只会看到“修改之后重启没效果”。
排查方法不难。在 gsetupmod 里查询目标变量的大小,同时留意当前环境的剩余变量空间。如果剩余空间很少,建议只改和你需求最相关的几个选项,不要贪多;如果空间已经满了,需要通过固件自带的重置功能清理冗余变量,或者先恢复默认设置,给后续修改腾出空间。
4.3 不同厂商的变量命名和偏移没有统一规律
ARM 平台固件碎片化的问题,在变量命名上体现得非常明显。同一个功能,Intel 参考实现里可能叫 Setup.DvmtPreAlloc,某 ARM 厂商的固件里可能叫 VarSetup.DvmtSize,还有的会在 GUID 上做文章,直接用私有 GUID。这就意味着,网上的现成教程只能作为参考,不能直接照抄。你在自己的板子上看到变量列表后,最好先用“选项名称”去反查“变量名”,确认对应关系,再动笔改。
关于“D大魔改BIOS”这类词,很多朋友可能会想到各种论坛里流传的定制版 BIOS。但说句掏心窝的话,真正的好固件不是靠神秘参数魔改出来的,而是靠理解设备固件逻辑之后,做精准、克制的调整。gsetupmod 这类工具的价值,恰恰在于让你能自己掌控这套逻辑,而不是依赖别人的成品。
4.4 常见问题速查表
| 现象 | 可能原因 | 解决建议 |
|---|---|---|
| 修改后重启没有新菜单 | 条件变量不止一个 | 查询所有关联变量,逐一修改 |
| 变量写入了,但值被强制恢复 | 固件启动流程会重置默认值 | 检查默认变量表存储位置,改默认值 |
| 修改其他选项时会闪退 | 变量空间不足 | 清理冗余变量后再试 |
| 找不到任何隐藏选项 | 固件已签名/加密封装 | 确认固件是否加密,可能需要先解包 |
| Secure Boot 开启时报签名错误 | 变量修改破坏了签名链 | 暂时关闭签名验证或恢复原变量,再重新修改 |
还有一个容易被忽略的问题:部分 ARM 笔记本和超薄设备使用的主控固件不是标准 UEFI,而是厂商定制的轻量级引导环境,这类设备并不适用于本文介绍的流程。开始操作前,先确认你的设备确实运行的是 UEFI 固件,可以在启动时看有没有 UEFI Shell 入口,或者查一下设备规格是否标注支持 UEFI 启动。如果是纯定制引导环境,gsetupmod 帮不上忙,得换另一套思路。
5. 后续还能怎么玩,以及几点个人体会
把 ARM 平台的隐藏菜单打开之后,你会发现可玩性比想象中大不少。串口调试开关、PCIe 链路策略、ACPI 日志等级、部分芯片组功耗调度选项,这些都是平时很难通过操作系统层面改的,一旦能在 Setup 页面直接操作,调试和优化效率会高很多。特别是做嵌入式开发的朋友,某些板卡的串口重定向选项隐藏得很深,有了 gsetupmod 就能快速打开,不用再为了一个调试口去折腾编译整套固件。
我个人实际用下来的体会是,gsetupmod 的 ARM 版本已经可以胜任绝大部分常见场景,但毕竟项目还年轻,对某些厂商的私有扩展做适配仍需时间。建议在动手之前,先把当前固件完整备份一份,并把原始的变量清单导出来保存,这样每次修改都有据可查,也方便回滚。改 BIOS 从来不是为了炫技,而是在可控范围内解决实际问题。相比直接刷别人做好的定制固件,自己动手定位隐藏项、修改变量、反复验证,这个过程虽然慢一些,但对设备逻辑的理解会深一个层次。
最后提醒一点,任何固件级别的修改都存在风险,尤其是 ARM 平台设备,很多没有双 BIOS 设计,刷坏了恢复成本高。如果不是必须解锁某个隐藏项,完全可以不去动它;如果确实需要,那就按“备份 → 查询 → 小范围修改 → 验证”的顺序来,不要一上来就批量开启所有隐藏项。工具只是手段,稳才是第一位。