微碼

出自 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[編輯 | 編輯原始碼]

參見 Unified kernel image#Manually 以獲取將微碼包含在單一的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

參見[編輯 | 編輯原始碼]