Microcode (简体中文) - ArchWiki

Jump to content
翻译状态：本文是 Microcode 的翻译。上次翻译日期：2022-07-20。如果英文版本有所更改，则您可以帮助同步翻译。

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

使用 AMD 或 Intel CPU 的用户都应该安装微码更新以确保系统稳定性。

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

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

## 早期加载

### 安装

根据处理器，安装以下软件包：

- AMD 处理器安装 amd-ucode。
- Intel 处理器安装 intel-ucode。

微码必须由引导加载程序加载。由于用户的早期启动配置存在很大差异，Arch 的默认配置可能不会自动触发微码更新。许多 AUR 内核在这方面都遵循官方 Arch 内核。

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

在下面的部分中，把 `cpu_manufacturer` 换成 CPU 的制造商，即 `amd` 或 `intel`。

提示： 对于安装在可移动设备的 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-ucode 和 intel-ucode 更新。否则需要在微码包更新时手动将 `/boot/cpu_manufacturer-ucode.img` 复制到 ESP。

#### Unified kernel images

参见 Unified kernel image#Manually。

#### EFISTUB

追加两个 `initrd=` 选项：

    initrd=\cpu_manufacturer-ucode.img initrd=\initramfs-linux.img

#### rEFInd

编辑 `/boot/refind_linux.conf` 中的引导选项并将 `initrd=boot\cpu_manufacturer-ucode.img`（如果 `/boot` 是一个单独的分区，则是 `initrd=cpu_manufacturer-ucode.img`）添加为第一个 initramfs。例如：

    "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"

提示： 以前未指定 `initrd` 内核参数的用户需要按照 rEFInd (简体中文)#配置中描述的步骤来启用多个 `initrd` 参数的传递。

如果在 `esp/EFI/refind/refind.conf` 中通过手动配置定义所要引导的内核，那么只需将 `initrd=boot\cpu_manufacturer-ucode.img`（如果 `/boot` 是一个单独的分区，则是 `initrd=cpu_manufacturer-ucode.img`）添加到 options 一行，不要修改配置的主干部分。例如：

    options  "root=PARTUUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX rw add_efi_memmap initrd=boot\cpu_manufacturer-ucode.img"

#### Syslinux

注意： 在 `cpu_manufacturer-ucode.img` 和 `initramfs-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.img` 和 `initramfs-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

## 后期加载

警告： 后期微码加载被认为是危险的，在 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/`。

### 启用微码更新的后期加载

与早期加载不同，Arch Linux 上微码更新的后期加载通过 `/usr/lib/tmpfiles.d/linux-firmware.conf` 默认启用。启动后，文件由 systemd-tmpfiles-setup.service(8) 解析，并更新 CPU 微码。

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

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

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

### 禁用微码更新的后期加载

对于 AMD 系统来说，即使不安装 amd-ucode，CPU 微码仍然会被更新，因为 `/usr/lib/firmware/amd-ucode/` 中的文件由 linux-firmware 提供（FS#59840）。和容器（FS#46591）不支持更新 CPU 微码，所以你可能想禁用微码更新。

要禁用危险的晚期微码更新，需要覆盖 linux-firmware 提供的 `/usr/lib/tmpfiles.d/linux-firmware.conf` 这个 tmpfile。这可以通过在 `/etc/tmpfiles.d/` 中创建同名的文件来完成：

    # ln -s /dev/null /etc/tmpfiles.d/linux-firmware.conf

## 内置于 initramfs 中的微码

如果您使用的 initramfs 生成器已经将微码 cpio 放在 initramfs 的最前面，则不需要#早期加载和#后期加载。例如，dracut 已经默认这样做；参见 dracut.conf(5) § DESCRIPTION。

## 验证微码已在启动时更新

使用 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 的页面查看是否支持特定型号：

- Gentoo:AMD microcode#Specific firmware only。
- Intel 下载中心。

### 检查可用的微码更新

可以用 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 -

5. 如果有更新可用，它应该会在 selected microcodes 下显示
6. 微码可能已经在你的 BIOS 里，所以不会在 dmesg 里出现。和正在运行的微码对比：`grep microcode /proc/cpuinfo`

## 参见

- 更新微码——社区经验
- Notes on Intel Microcode updates – Ben Hawkes
- 内核微码加载器——内核文档
- Erratum found in Haswell/Broadwell – AnandTech
- iucode-tool GitLab 项目

Categories:

- CPU (简体中文)
- Security (简体中文)
- Virtualization (简体中文)