1. 先搞清楚这两个"Secure Boot 状态"到底是谁在说话
很多人第一次碰到这个现象的时候,反应都差不多:明明进 BIOS 看到 Secure Boot 那一栏写着 Enabled,进系统敲一条命令,返回的却是SecureBoot disabled。于是开始怀疑是不是主板骗人、是不是系统装歪了、是不是固件有 bug。我当年第一次遇到也折腾了大半天,后来才明白,这俩"状态"压根不是同一个东西,它们分别由两个不同的角色、在不同的时机、用不同的口径在汇报。
先把结论摆前面:BIOS 里显示的是"固件层面的开关状态",Linux 里读到的是"内核实际生效的安全启动状态"。这两者绝大多数情况下是一致的,但只要有任何一个中间环节掉了链子,就会出现"上面说开、下面说关"的错位。你要做的不是纠结谁对谁错,而是顺着这条链路一层层往下查,找到那个断点。
这篇文章我打算把这条链路完整拆开讲:从固件里的开关,到 UEFI 变量,到内核怎么读、怎么判定,再到常见的几种错位场景和排查手法。适合两类人看——一类是刚接触 UEFI 安全启动、被这个现象搞懵的新手;另一类是已经知道大概原理,但想搞清楚"到底哪一步断了"的运维和系统玩家。看完你应该能自己定位问题,而不是靠重启碰运气。
在展开之前,先统一几个概念,不然后面容易绕晕。Secure Boot(安全启动)是 UEFI 规范里的一套机制,核心目的是确保开机时加载的引导程序和内核镜像是被信任签名的,防止有人往引导链里塞东西。它依赖一组叫UEFI 变量的东西来记录状态,其中最关键的两个是SecureBoot和SetupMode。而 Linux 这边,内核启动后会去读这些变量,把结果通过/sys/firmware/efi/efivars/暴露出来,同时mokutil、bootctl、dmesg这些工具会给你一个"人话版"的结论。错位,就发生在"固件写的"和"内核读到的"之间。
2. 固件里的"已启用"到底意味着什么
2.1 BIOS 界面上的开关只是"意图声明"
你在 BIOS/UEFI 设置界面里看到的那个 Secure Boot 选项,本质上是固件给你看的一个配置意图。它告诉你:固件当前被设置成"我希望启用安全启动"。但注意,这只是意图,不是结果。固件要真正让安全启动生效,还需要满足一堆前置条件,比如平台密钥(PK)、密钥交换密钥(KEK)、签名数据库(db)这些变量都得正确写入,而且不能处于 Setup Mode。
换句话说,BIOS 里那个 Enabled 更像是你在一张申请表上勾了"我要办这个业务",但业务到底办没办成,得看后台有没有真正受理。很多主板厂商的界面做得比较"乐观",只要你没手动关掉,它就显示 Enabled,哪怕底下的密钥其实没配全、或者被清空了,它照样显示 Enabled。这就是错位的第一大来源。
2.2 固件状态其实分好几种,不止"开/关"
真正懂 UEFI 的人不会只用"开/关"来描述安全启动,因为固件内部至少有这几种状态:
| 固件内部状态 | 含义 | BIOS 界面常见显示 |
|---|---|---|
| Setup Mode | 密钥未写入或已被清除,处于可配置状态 | 有时仍显示 Enabled,有时显示 Setup |
| User Mode + SecureBoot 开 | 密钥齐全,安全启动真正生效 | Enabled |
| User Mode + SecureBoot 关 | 密钥齐全,但开关被关掉 | Disabled |
| 密钥被清空 | PK/KEK/db 被删,退回 Setup Mode | 视厂商而定,可能仍显示 Enabled |
看到没,"密钥被清空"这一行就是重灾区。很多主板在你执行"恢复默认设置"或者刷完 BIOS 之后,会把密钥清掉,退回 Setup Mode,但界面上的 Secure Boot 那一栏还是显示 Enabled。这时候你进 Linux 一查,内核读到SetupMode=1,自然就判定安全启动没生效,返回 disabled。两边都没说谎,只是说的不是一回事。
2.3 为什么厂商界面不把真实状态显示清楚
这个问题我被问过很多次。原因其实很现实:BIOS 界面是给"配置"用的,不是给"诊断"用的。厂商假设你进来就是要改设置,所以它显示的是"当前设置值",而不是"运行时实际状态"。而且不同厂商对 Setup Mode 的界面呈现策略不一样,有的会明确标出来,有的干脆不显示,导致用户根本不知道密钥已经没了。
提示:如果你怀疑是密钥问题,别在 BIOS 界面里瞎找,直接进 Linux 用命令读 UEFI 变量,那个才是真相。
3. Linux 里的 disabled 是怎么算出来的
3.1 内核读的是 UEFI 变量,不是 BIOS 界面
Linux 判断安全启动状态,靠的是读 UEFI 运行时变量。最直接的两个文件在:
/sys/firmware/efi/efivars/SecureBoot-<GUID> /sys/firmware/efi/efivars/SetupMode-<GUID>这两个文件的内容是一个字节(准确说是 4 字节属性 + 1 字节数据),最后那个字节如果是1就代表真,0就代表假。内核在启动早期会读这两个变量,然后决定SecureBoot这个全局标志是开还是关。你后面用的所有工具,本质上都是在读这个内核标志,或者重新去读这两个变量。
所以链路是这样的:
BIOS 设置界面(意图) ↓ 固件写入 UEFI 变量 SecureBoot / SetupMode(事实) ↓ 内核启动时读取 内核全局标志(判定) ↓ 工具查询 mokutil / bootctl / dmesg(呈现)任何一层出问题,最终呈现就会和 BIOS 界面不一致。下面我逐个拆。
3.2 用命令直接看原始变量,别只看结论
很多人上来就敲mokutil --sb-state,看到SecureBoot disabled就慌了。其实你应该先看原始变量,那才是第一手证据:
# 看 SecureBoot 变量,最后一位是数据 od -An -t u1 /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c # 看 SetupMode 变量 od -An -t u1 /sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c如果SecureBoot最后一位是0,说明固件层面就没开;如果SetupMode是1,说明密钥没配全,安全启动不可能生效。这两个一读,基本就能定位是固件侧的问题还是系统侧的问题。
3.3 内核日志里其实早就写了原因
还有一个被严重低估的信息源:dmesg。内核在启动时如果发现安全启动相关的东西有问题,会往日志里写。你可以这样过滤:
dmesg | grep -i -E "secure|efi|setup"常见的有这么几类输出,含义完全不同:
Secure boot enabled:内核确认安全启动生效,和 BIOS 一致。Secure boot disabled:内核读到 SecureBoot=0,固件层面就是关的。Secure boot disabled (Setup Mode):密钥没配全,退回 Setup Mode。- 完全没有相关行:可能是内核没走 EFI 启动,或者变量读取失败。
最后那种"完全没有"最坑,因为它意味着你可能压根不是 UEFI 启动,而是 Legacy/CSM 模式启动的。这种情况下/sys/firmware/efi目录可能都不存在,安全启动自然无从谈起。
4. 六种典型错位场景,对号入座
我把这些年遇到过的"BIOS 说开、Linux 说关"的情况归了归类,基本逃不出下面六种。你可以拿着自己的现象对号入座,能省不少时间。
4.1 场景一:密钥被清空,退回 Setup Mode
这是最常见的一种。典型触发动作是:恢复 BIOS 默认设置、刷固件、换主板电池、手动执行过"清除安全启动密钥"。现象是 BIOS 界面 Secure Boot 仍显示 Enabled,但 Linux 里SetupMode=1,mokutil报 disabled。
判断方法就是读SetupMode变量,是1就实锤了。解决办法是进 BIOS 重新执行"恢复出厂密钥"或者"安装默认密钥",把 PK/KEK/db 写回去,退出 Setup Mode。有些主板这个选项藏得很深,可能在 Security 或者 Boot 子菜单里,名字叫Restore Factory Keys、Install Default Secure Boot Keys之类。
4.2 场景二:系统其实是 Legacy/CSM 启动的
这个也很常见,尤其是老机器或者装系统时没注意。BIOS 里 Secure Boot 开着,但你的系统是用 Legacy BIOS 或者 CSM 兼容模式引导的。这种情况下内核根本没走 UEFI 路径,读不到 UEFI 变量,自然报 disabled。
判断方法很简单:
ls /sys/firmware/efi如果这个目录不存在,或者efibootmgr命令报错说没有 EFI 支持,那你就是 Legacy 启动。解决办法是进 BIOS 关掉 CSM,确保从 UEFI 分区引导,必要时用efibootmgr重建引导项。
4.3 场景三:引导链中间有未签名的环节
安全启动是整条链都要验签的:固件验引导程序,引导程序验内核,内核验模块。如果中间任何一个环节没签名或者签名不被信任,安全启动会在那一环失败。但这里有个微妙的地方:失败的方式不同,呈现也不同。
如果是引导程序没签名,固件直接拒绝加载,你根本进不了系统,会看到安全启动违规的报错。但如果是内核模块没签名,系统能起来,只是那个模块加载失败,而mokutil可能仍然报 enabled。真正会导致"BIOS 开、系统报 disabled"的,通常是引导程序这一环被跳过或者用了 shim 之类的中间层,导致内核读到的状态和固件不一致。
4.4 场景四:虚拟机或容器环境的特殊性
如果你是在虚拟机里跑 Linux,那 BIOS 界面和 Linux 里的状态不一致就更正常了。虚拟机的"固件"是宿主模拟出来的,安全启动支持参差不齐。有的虚拟化平台默认不开安全启动,但它的设置界面可能显示得模棱两可;有的开了但没配密钥,内核读到的就是 Setup Mode。
判断方法还是那套:读变量、看 dmesg。虚拟化环境下别指望界面,一切以变量为准。
4.5 场景五:内核版本或配置差异
极少数情况下,是内核本身的问题。比如某些定制内核编译时没开 EFI 相关选项,或者启动参数里加了efi=disable_early_pci_dma之类影响 EFI 初始化的东西,导致内核读不到变量。这种比较少见,但排查时可以用uname -r确认内核版本,用cat /proc/cmdline看启动参数有没有异常。
4.6 场景六:双系统引导器"劫持"了引导链
装了双系统的机器上,如果引导器(比如某个第三方引导管理程序)不是被安全启动信任的,它可能会用各种方式绕过验签。绕过之后,内核虽然起来了,但它读到的安全启动状态可能是被"降级"过的。这种场景排查起来最绕,建议直接看dmesg里 EFI 相关的完整输出,顺着引导链一段段确认。
5. 一套可复现的排查流程
光讲场景不够,我给你一套我自己常用的排查顺序,照着走基本能定位。整个过程不需要重启太多次,大部分信息在系统里就能拿到。
5.1 第一步:确认是不是 UEFI 启动
# 方法一:看目录 ls -d /sys/firmware/efi 2>/dev/null && echo "UEFI" || echo "Legacy" # 方法二:看 efibootmgr efibootmgr 2>/dev/null | head -5如果这一步就判定是 Legacy,那后面都不用查了,问题根源就是启动模式不对。进 BIOS 关 CSM,改成纯 UEFI 引导。
5.2 第二步:读原始 UEFI 变量
SB_GUID="8be4df61-93ca-11d2-aa0d-00e098032b8c" echo -n "SecureBoot: "; od -An -t u1 /sys/firmware/efi/efivars/SecureBoot-$SB_GUID | awk '{print $NF}' echo -n "SetupMode: "; od -An -t u1 /sys/firmware/efi/efivars/SetupMode-$SB_GUID | awk '{print $NF}'对照结果:
| SecureBoot | SetupMode | 结论 |
|---|---|---|
| 1 | 0 | 安全启动真正生效,和 BIOS 一致 |
| 0 | 0 | 固件层面就是关的,去 BIOS 开 |
| 0 | 1 | 密钥没配全,退回 Setup Mode |
| 1 | 1 | 矛盾状态,固件可能有 bug,考虑刷固件 |
5.3 第三步:交叉验证工具输出
mokutil --sb-state bootctl status 2>/dev/null | grep -i secure dmesg | grep -i -E "secure boot|setup mode"三个工具的输出如果一致,那结论就稳了。如果不一致,以原始变量为准,工具可能有缓存或者版本差异。
5.4 第四步:根据结论决定动作
- 如果是 Setup Mode:进 BIOS 恢复密钥。
- 如果是 Legacy 启动:改 UEFI 引导。
- 如果是固件矛盾状态:考虑更新固件。
- 如果一切正常但工具报错:检查工具版本和内核配置。
注意:改 BIOS 设置前,先记下当前引导项顺序,免得改完进不去系统。我踩过这个坑,改完 CSM 之后引导项全乱了,折腾了半小时才恢复。
6. 密钥管理这块,新手最容易栽的坑
6.1 PK、KEK、db 到底谁管谁
安全启动的密钥体系是分层的,很多人搞不清这三个的关系。简单说:
- PK(Platform Key):最顶层,一个平台只有一把,代表平台所有者。
- KEK(Key Exchange Key):用来签名 db 和 dbx 的更新,可以有多把。
- db(Signature Database):真正用来验签引导程序和内核的白名单。
- dbx(Forbidden Database):黑名单,被吊销的签名放这里。
链路是 PK 授权 KEK,KEK 授权 db/dbx 的更新。所以如果你把 PK 清了,整个体系就塌了,直接退回 Setup Mode。这也是为什么"清除密钥"操作要慎之又慎。
6.2 自己签名内核模块的正确姿势
如果你需要加载自己编译的内核模块,又不想关安全启动,那就得自己签名,然后把公钥注册进 MOK(Machine Owner Key)。流程大致是:
# 生成密钥 openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der -nodes -days 36500 -subj "/CN=My Key/" # 签名模块 /usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 MOK.priv MOK.der your_module.ko # 注册公钥(会要求重启进 MOK 管理界面确认) mokutil --import MOK.der重启后会进一个蓝色的 MOK 管理界面,需要你手动确认导入。这一步很多人以为敲完命令就完事了,结果重启没注意,公钥没导进去,模块还是加载不了。
6.3 别轻易关安全启动来"图省事"
我见过太多人一遇到模块加载失败,第一反应就是进 BIOS 把安全启动关了。短期确实省事,但长期看是把整个引导链的防护都拆了。正确做法是签名 + 注册 MOK,一次配置好,后面就顺了。而且现在主流发行版对第三方模块的签名支持都挺成熟,没必要因噎废食。
7. 常见问题速查表
把高频问题整理成表,方便你直接查。
| 现象 | 最可能原因 | 快速验证 | 解决方向 |
|---|---|---|---|
| BIOS 显示 Enabled,mokutil 报 disabled | 密钥被清空,Setup Mode | 读 SetupMode 变量 | BIOS 恢复出厂密钥 |
| 同上,但 SetupMode=0 | 固件层面 SecureBoot=0 | 读 SecureBoot 变量 | BIOS 里重新开启 |
| /sys/firmware/efi 不存在 | Legacy/CSM 启动 | ls 该目录 | 改纯 UEFI 引导 |
| dmesg 无 secure 相关行 | 内核没走 EFI 路径 | 看 /proc/cmdline | 检查引导模式 |
| 模块加载失败但系统能起 | 模块未签名 | dmesg 看模块报错 | 签名 + 注册 MOK |
| 双系统下状态飘忽 | 引导器绕过验签 | 看完整 EFI 日志 | 统一引导链 |
8. 我个人的几条实操心得
折腾安全启动这些年,有几个体会是文档里不会写的,分享给你。
第一,永远以 UEFI 变量为准,别信界面。BIOS 界面是给人配置用的,不是给人诊断用的。养成进系统先读变量的习惯,能省掉大量"重启进 BIOS 看一眼"的无用功。
第二,改 BIOS 前先备份引导项。efibootmgr -v的输出截个图存着,改完设置如果引导乱了,照着恢复。这个习惯帮我救过好几次场。
第三,Setup Mode 不等于故障。它只是一个状态,说明密钥没配。很多新手看到 Setup Mode 就以为主板坏了,其实进 BIOS 点一下恢复密钥就好了。理解状态机的意义,比记住具体操作更重要。
第四,虚拟化环境别套用物理机的经验。虚拟机的固件行为差异很大,安全启动支持也参差不齐。在虚拟机里排查,先确认虚拟化平台本身对安全启动的支持程度,再往下查。
第五,签名这件事一次配好,长期受益。自己签模块、注册 MOK,前期花半小时,后面所有自编译模块都能顺畅加载,不用反复关安全启动。这笔账怎么算都划算。
最后再补一句关于排查心态的:这个"BIOS 说开、系统说关"的现象,本质上是信息源不一致,不是谁坏了。你要做的是找到那个断点,而不是急着下结论说主板有问题或者系统有问题。顺着固件到变量到内核这条链路走一遍,答案自然就出来了。