24. Linux 微码加载器¶
- 作者:
Fenghua Yu <fenghua.yu@intel.com>
Borislav Petkov <bp@suse.de>
Ashok Raj <ashok.raj@intel.com>
内核具有 x86 微码加载工具,旨在提供操作系统中的微码加载方法。潜在的用例包括在 OEM 生命周期支持之外的平台上更新微码,以及在无需重启的情况下更新长时间运行的系统上的微码。
加载器支持三种加载方法
24.1. 早期加载微码¶
内核可以在启动过程中很早的时候更新微码。 早期加载微码可以修复 CPU 问题,避免在内核启动时出现问题。
微码存储在 initrd 文件中。 在启动期间,从 initrd 文件读取微码并将其加载到 CPU 核心中。
组合 initrd 镜像的格式是(未压缩)cpio 格式的微码,后跟(可能已压缩)的 initrd 镜像。 加载器在启动期间解析组合的 initrd 镜像。
cpio 命名空间中的微码文件为
- 在 Intel 上
kernel/x86/microcode/GenuineIntel.bin
- 在 AMD 上
kernel/x86/microcode/AuthenticAMD.bin
在 BSP (引导处理器) 启动期间(pre-SMP),内核会扫描 initrd 中的微码文件。 如果找到与 CPU 匹配的微码,它将应用于 BSP,然后应用于所有 AP (应用程序处理器)。
加载器还会将与 CPU 匹配的微码保存在内存中。 因此,当 CPU 从睡眠状态恢复时,会应用缓存的微码补丁。
这是一个粗略的示例,说明如何准备带有微码的 initrd(通常由发行版自动完成,在重新创建 initrd 时,因此您实际上不必自己完成。 此处仅供将来参考记录)。
#!/bin/bash
if [ -z "$1" ]; then
echo "You need to supply an initrd file"
exit 1
fi
INITRD="$1"
DSTDIR=kernel/x86/microcode
TMPDIR=/tmp/initrd
rm -rf $TMPDIR
mkdir $TMPDIR
cd $TMPDIR
mkdir -p $DSTDIR
if [ -d /lib/firmware/amd-ucode ]; then
cat /lib/firmware/amd-ucode/microcode_amd*.bin > $DSTDIR/AuthenticAMD.bin
fi
if [ -d /lib/firmware/intel-ucode ]; then
cat /lib/firmware/intel-ucode/* > $DSTDIR/GenuineIntel.bin
fi
find . | cpio -o -H newc >../ucode.cpio
cd ..
mv $INITRD $INITRD.orig
cat ucode.cpio $INITRD.orig > $INITRD
rm -rf $TMPDIR
系统需要在 /lib/firmware 中安装了微码包,否则您需要修复上面的路径,如果您的微码包位于其他位置,或者您直接从处理器供应商的站点下载了它们。
24.2. 延迟加载¶
只需安装你的发行版提供的微码包并运行
# echo 1 > /sys/devices/system/cpu/microcode/reload
作为 root 用户。
加载机制在 /lib/firmware/{intel-ucode,amd-ucode} 中查找微码 blob。 默认的发行版安装包已将其放置在那里。
自内核 5.19 以来,默认情况下不启用延迟加载。
/dev/cpu/microcode 方法已在 5.19 中删除。
24.3. 为什么延迟加载很危险?¶
24.3.1. 同步所有 CPU¶
接收微码更新的微码引擎在 SMT 系统中的两个逻辑线程之间共享。 因此,当在核心的一个 SMT 线程上执行更新时,兄弟线程会“自动”获得更新。
由于微码也可以“模拟” MSR,因此在微码更新过程中,这些模拟的 MSR 会暂时停止存在。 如果 SMT 兄弟线程恰好正在访问此类 MSR,则可能导致不可预测的结果。 通常的观察是,此类 MSR 访问会导致 #GP 异常,以表明前者不存在。
消失的 MSR 只是一个常见的被观察到的问题。 任何其他正在被修补并由其他 SMT 兄弟线程并发执行的指令也可能导致类似、不可预测的行为。
为了消除这种情况,引入了一种基于 stop_machine() 的 CPU 同步,作为保证所有逻辑 CPU 都不会执行任何代码,而只是在自旋循环中等待,轮询原子变量的方法。
虽然这解决了设备或外部中断、包括 LVT 中断(例如 CMCI 等)的 IPI,但它无法解决其他无法关闭的特殊中断。 这些是机器检查 (#MC)、系统管理 (#SMI) 和非屏蔽中断 (#NMI)。
24.3.2. 机器检查¶
机器检查 (#MC) 是不可屏蔽的。 有两种类型的 MCE。 致命的不可恢复的 MCE 和可恢复的 MCE。 虽然不可恢复的错误是致命的,但在内核上下文中发生的可恢复错误也被内核视为致命错误。
在某些 Intel 机器上,MCE 也会广播到系统中的所有线程。 如果一个线程正在执行 WRMSR,则将在流程结束时获取一个 MCE。 无论哪种方式,如果系统中的任何线程未能签入 MCE 交汇点,它们将等待执行 wrmsr(0x79) 的线程在 MCE 处理程序中会合并最终关闭。
为了偏执并获得可预测的行为,操作系统可以选择设置 MCG_STATUS.MCIP。 由于一个系统中最多只能有一个 MCE,因此如果发出了 MCE 信号,则上述条件将自动升级为系统重置。 操作系统可以在该核心更新结束时关闭 MCIP。
24.3.3. 系统管理中断¶
SMI 也广播到平台中的所有 CPU。 微码更新请求在写入 MSR 0x79 之前对核心的独占访问。 因此,如果一个线程正在 WRMSR 流程中,而第二个线程收到了一个 SMI,则该线程将在 SMI 处理程序中的第一条指令中停止。
由于辅助线程在 SMI 中的第一条指令中停止,因此它几乎不可能正在执行一条正在被修补的指令。 此外,操作系统无法阻止 SMI 发生。
24.3.4. 非屏蔽中断¶
当核心的 thread0 正在进行微码更新时,如果 thread1 被拉入 NMI,则由于上述原因,可能会导致不可预测的行为。
操作系统可以选择多种方法来避免遇到这种情况。
24.3.5. 微码适合延迟加载吗?¶
当系统完全运行并运行实际工作负载时,会进行延迟加载。 延迟加载行为取决于在升级到新补丁之前 CPU 上的基本补丁是什么。
对于 Intel CPU 来说,这是真的。
例如,假设一个 CPU 具有补丁级别 1,并且更新为补丁级别 3。
在 patch1 和 patch3 之间,patch2 可能已弃用软件可见的功能。
如果软件甚至可能正在使用该功能,这是不可接受的。 例如,假设 MSR_X 在更新后不再可用,访问该 MSR 将导致 #GP 错误。
基本上,没有办法声明新的微码更新适合延迟加载。 这是导致默认情况下不启用延迟加载的另一个问题。
24.4. 内置微码¶
加载器还支持加载通过常规内置固件方法 CONFIG_EXTRA_FIRMWARE 提供的内置微码。 目前仅支持 64 位。
这是一个例子
CONFIG_EXTRA_FIRMWARE="intel-ucode/06-3a-09 amd-ucode/microcode_amd_fam15h.bin"
CONFIG_EXTRA_FIRMWARE_DIR="/lib/firmware"
这基本上意味着,您在本地具有以下树结构
/lib/firmware/
|-- amd-ucode
...
| |-- microcode_amd_fam15h.bin
...
|-- intel-ucode
...
| |-- 06-3a-09
...
以便构建系统可以找到这些文件并将它们集成到最终内核映像中。 早期加载器找到它们并应用它们。
不用说,这种方法并不是最灵活的一种,因为它需要每次从 CPU 供应商获得更新的微码时都重新构建内核。