微码

来自 Arch Linux 中文维基

处理器制造商会发布对处理器微码的稳定性和安全性更新。这些更新提供了对系统稳定性至关重要的错误修复。如果没有这些更新,则可能会遇到不明原因的崩溃或难以跟踪的意外停机。

使用 AMD 或 Intel CPU 的用户都应该安装微码更新以确保系统稳定性。在虚拟机或容器中时,微码更新应在主机上实施,而不是客户机.

加载微码[编辑 | 编辑源代码]

微码更新通常随主板固件一起提供,并在固件初始化时被应用。但是 OEM 可能不会及时发布固件更新,并且旧系统根本不会获得新的固件更新,所以 Linux 内核提供了启动时应用微码更新的功能。Linux 微码加载器支持三种加载方式:

  1. 内置微码可以编译到内核中,然后由早期加载程序应用。
  2. 早期加载在启动过程中很早就更新微码(比 initramfs 阶段还早),所以是推荐的方式。对于具有严重硬件错误的 CPU,例如 Intel Haswell 和 Broadwell 处理器系列,必须选择这种方式。
  3. 后期加载危险!)在启动后更新微码,这可能太晚了,因为 CPU 可能已经使用了有问题的指令集。即使已经使用了早期加载,后期加载依然有价值,可以在系统不重启的情况下应用较新的微码更新。

根据不同的处理器,安装以下软件包之一以获取微码:

initramfs中内建微码[编辑 | 编辑源代码]

这篇文章的某些内容需要扩充。

原因:
  • This should have a change in structure splitting unified kernel images like the output of dracut or a manually/"voluntarily" generated one and the other images like the default configuration of Booster or mkinitcpio.
  • The introduction does not cover this section.
(在 Talk:微码 中讨论)

可以将微码构入 initramfs. 一些 initramfs 生成器, 比如 dracut (见 dracut.conf(5) § DESCRIPTION), 默认如此. 这使得 #Early loading#Late loading 不再必要. 然而,Arch Linux 默认使用 mkinitcpio 来生成初始化 ramdisk (initramfs), 并不支持这个.但他支持通过 UEFI stub 加载微码: 见 #Unified kernel images.

早期加载[编辑 | 编辑源代码]

如果没有编译到内核中,微码必须早期加载. 他可以作为unified kernel image的一部分,或作为一个initrd image 传递给你的 boot loader 并被其加载.

请注意,由于用户的早期启动配置存在很大差异,Arch 的默认配置可能不会自动触发微代码更新。

Unified kernel images[编辑 | 编辑源代码]

参见统一内核镜像以获取将微码包含在单一的 image 中的指导。

Initrd images[编辑 | 编辑源代码]

必须通过把 /boot/amd-ucode.img/boot/intel-ucode.img 添加为引导加载程序配置文件中的第一个 initrd 启用这些更新。即在正常的 initrd 文件之前。下文包含常见引导加载程序的说明。

在下面的部分中,把 cpu_manufacturer 换成你的 CPU 的制造商,即 amdintel

提示:对于安装在可移动设备的 Arch Linux,由于可能会在这些处理器中的任何一个上运行,因此两个软件包都要安装,并且两个微码文件都要加到配置文件里,两者的顺序并不重要,只要都在 initramfs 镜像之前就行。
在自定义内核中启用早期微码加载[编辑 | 编辑源代码]

为了在自定义内核中进行早期加载,需要将 "CPU microcode loading support" 编译到内核中,而不是编译为模块。这将启用 "Early load microcode" 提示,将其设置为 Y

CONFIG_BLK_DEV_INITRD=Y
CONFIG_MICROCODE=y
CONFIG_MICROCODE_INTEL=Y
CONFIG_MICROCODE_AMD=y
GRUB[编辑 | 编辑源代码]

grub-mkconfig 会自动检测微码更新并正确配置 GRUB。安装微码软件包后,重新生成 GRUB 配置以激活加载微码更新:

# grub-mkconfig -o /boot/grub/grub.cfg

或者,如果手动管理 GRUB 配置文件,可以添加 /boot/cpu_manufacturer-ucode.img(如果 /boot 是一个单独的分区,则是 /cpu_manufacturer-ucode.img):

/boot/grub/grub.cfg
...
echo 'Loading initial ramdisk'
initrd	/boot/cpu_manufacturer-ucode.img /boot/initramfs-linux.img
...

对每个菜单项重复此操作。

systemd-boot[编辑 | 编辑源代码]

在初始 ramdisk 之前使用 initrd 选项加载微码,如下所示:

/boot/loader/entries/entry.conf
title   Arch Linux
linux   /vmlinuz-linux
initrd  /cpu_manufacturer-ucode.img
initrd  /initramfs-linux.img
...

最新的 cpu_manufacturer-ucode.img 在启动时必须存在于 EFI 系统分区(ESP)中。ESP 必须挂载到 /boot 才能让微码随 amd-ucodeintel-ucode 更新。否则需要在每次微码包更新时手动将 /boot/cpu_manufacturer-ucode.img 复制到 ESP。

EFISTUB[编辑 | 编辑源代码]

追加两个 initrd= 选项:

initrd=\cpu_manufacturer-ucode.img initrd=\initramfs-linux.img
rEFInd[编辑 | 编辑源代码]
提示:以前未指定 initrd 内核参数的用户需要按照 rEFInd#配置中描述的步骤来启用多个 initrd 参数的传递。

编辑 /boot/refind_linux.conf ,将微码添加第一个initrd=选项,比如 initrd=boot\cpu_manufacturer-ucode.img(如果 /boot 是一个单独的分区,则是 initrd=cpu_manufacturer-ucode.img

例如:

"Boot using default options"  "root=PARTUUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX rw add_efi_memmap initrd=boot\cpu_manufacturer-ucode.img initrd=boot\initramfs-%v.img"
Manual boot stanzas[编辑 | 编辑源代码]

Users employing manual stanzas in esp/EFI/refind/refind.conf to define kernels should add the initrd= parameter with the proper path within the boot partition. This parameter is required as part of the options line, and not in the main part of the stanza. E.g.:

options  "root=PARTUUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX rw add_efi_memmap initrd=boot\cpu_manufacturer-ucode.img"
Syslinux[编辑 | 编辑源代码]
注意:cpu_manufacturer-ucode.imginitramfs-linux.img initrd 文件间不要用空格。INITRD 一行必须和下面的示例完全一样。

/boot/syslinux/syslinux.cfg 中,可以用逗号分隔多个 initrd:

LABEL arch
    MENU LABEL Arch Linux
    LINUX ../vmlinuz-linux
    INITRD ../cpu_manufacturer-ucode.img,../initramfs-linux.img
...
LILO[编辑 | 编辑源代码]

LILO(可能还有其他旧的引导加载程序)不支持多个 initrd 镜像。这时,必须将 cpu_manufacturer-ucode.imginitramfs-linux.img 合并成一个镜像。

警告: 每次更新内核后都要重新合并!
注意: 顺序非常重要。在生成的镜像中,原来的 initramfs-linux.img 镜像必须在 cpu_manufacturer-ucode.img 之后

可使用以下命令合并将两个镜像合并为 initramfs-merged.img

# cat /boot/cpu_manufacturer-ucode.img /boot/initramfs-linux.img > /boot/initramfs-merged.img

现在编辑 /etc/lilo.conf 以加载新镜像。

...
initrd=/boot/initramfs-merged.img
...

然后以 root 身份运行 lilo

# lilo
Limine[编辑 | 编辑源代码]

For Limine you will just need to add the path to the microcode through the MODULE_PATH option in your limine.cfg file. Here is an example:

limine.cfg
DEFAULT_ENTRY=1
TIMEOUT=3

:Arch
    COMMENT=Arch Linux

    PROTOCOL=linux
    KERNEL_PATH=boot:///vmlinuz-linux
    CMDLINE=root=UUID=c0748521-eca9-4f38-989c-43811b6e39a1 rw loglevel=3
    MODULE_PATH=boot:///cpu_manufacturer-ucode.img
    MODULE_PATH=boot:///initramfs-linux.img

后期加载[编辑 | 编辑源代码]

警告: 后期微码加载被认为是危险的,在 Linux 5.19 上使用会玷污内核。[1]

微码更新的后期加载发生在系统启动之后。它使用 /usr/lib/firmware/amd-ucode//usr/lib/firmware/intel-ucode/ 中的文件。在 Linux 5.19+ 上,后期加载需要使用 CONFIG_MICROCODE_LATE_LOADING=y 构建内核。

对于 AMD 处理器来说,微码更新文件由 linux-firmware 提供。

对于 Intel 处理器来说,没有软件包提供微码更新文件(FS#59841)。要使用后期加载,你需要从Intel 提供的压缩包里手动解压出 intel-ucode/

启用微码更新的后期加载[编辑 | 编辑源代码]

如果要手动重新加载微码,例如在更新 /usr/lib/firmware/amd-ucode//usr/lib/firmware/intel-ucode/ 中的微码文件后,请运行:

# echo 1 > /sys/devices/system/cpu/microcode/reload

这允许在不重新启动系统的情况下应用较新的微码更新。

验证微码已在启动时更新[编辑 | 编辑源代码]

使用 journalctl 检查内核消息,以查看微码是否已更新:

# journalctl -k --grep=microcode

在 Intel 系统上,每次启动时都应该看到类似于以下的内容,表明微码很早就更新了:

microcode: microcode updated early to revision 0xde, date = 2020-05-18
microcode: sig=0x806ec, pf=0x80, revision=0xde
microcode: Microcode Update Driver: v2.2.
注意: 显示的日期与安装的 intel-ucode 软件包的版本不对应。它确实显示了英特尔上次更新与正在更新的特定硬件相对应的微码的时间。

完全有可能,特别是对于较新的硬件,CPU 没有微码更新。在这种情况下,输出可能如下所示:

microcode: sig=0x806ec, pf=0x80, revision=0xde
microcode: Microcode Update Driver: v2.2.

在使用早期加载的 AMD 系统上,输出将类似于以下内容:

microcode: microcode updated early to new patch_level=0x0700010f
microcode: CPU0: patch_level=0x0700010f
microcode: CPU1: patch_level=0x0700010f
microcode: CPU2: patch_level=0x0700010f
microcode: CPU3: patch_level=0x0700010f
microcode: Microcode Update Driver: v2.2.

在使用后期加载的 AMD 系统上,输出将在重新加载微码之前显示旧微码的版本,并在重新加载微码后显示新微码的版本。它看起来像这样:

microcode: CPU0: patch_level=0x0700010b
microcode: CPU1: patch_level=0x0700010b
microcode: CPU2: patch_level=0x0700010b
microcode: CPU3: patch_level=0x0700010b
microcode: Microcode Update Driver: v2.2.
microcode: CPU2: new patch_level=0x0700010f
microcode: CPU0: new patch_level=0x0700010f
microcode: CPU1: new patch_level=0x0700010f
microcode: CPU3: new patch_level=0x0700010f
x86/CPU: CPU features have changed after loading microcode, but might not take effect.

哪些 CPU 可以接受微码更新[编辑 | 编辑源代码]

可以从 Intel 自己的网站或 Gentoo wiki 上 AMD 的页面查看是否支持特定型号:

检查可用的微码更新[编辑 | 编辑源代码]

可以用 iucode-tool 来检查 intel-ucode.img 是否包含适用于当前运行的 CPU 的微码镜像。

  1. 安装 intel-ucode(检测并不需要修改 initrd)
  2. 安装 iucode-tool
  3. 加载 cpuid 内核模块:
    # modprobe cpuid
  4. 解包微指令映像,并搜索你的 cpuid:
# bsdtar -Oxf /boot/intel-ucode.img | iucode_tool -tb -lS -
  1. 如果有更新可用,它应该会在 selected microcodes 下显示
  2. 微码可能已经在你的 BIOS 里,所以不会在 dmesg 里出现。和正在运行的微码对比:grep microcode /proc/cpuinfo

参见[编辑 | 编辑源代码]