在 ThinkPad X220 上使用 coreboot

我在前一篇文章結尾提到過 coreboot,它是一個可拓展的固件平臺(官網上是這麼說的,在 PC 上可以理解爲 BIOS/UEFI 之類的東西)。優點是開源(但是一般情況下還是需要一些閉源的 blob 才能正常工作,當然這是硬件廠商的鍋;有一個完全自由的分支叫 libreboot),快(起碼個人感覺是比原廠的快…),可以自己修改訂製。
但是由於一些原因比如 Boot Guard(還是硬件廠商的鍋)等等,不是所有機器都會有 coreboot 適配,具體的適配列表可以在這裏看到: Supported_Motherboards

(說到 Boot Guard,本來出發點挺好的,爲了安全嘛!但是問題是這東西無法關閉,給自定義造成了麻煩;安全性上的提升實際上也沒多少,已經有人找到繞過它的辦法了。總的來說呢,無用,只不過是給 coreboot 之類的開源固件的適配造成了麻煩。安全固然重要,但並不能犧牲用戶的自由,更不能強迫所有人都去用這坨翔。)

coreboot 的人也挺無奈的,intel 那邊不用說了,一直是大坨 blob 還有 ME 這個東西;AMD 現在也好不了多少,Ryzen 並沒有多開放。不是他們不想做適配,而是做不出來… 這也是爲什麼一般只有老硬件纔會有 coreboot 甚至 libreboot 用。

幸運的是我有一臺 ThinkPad X220,實際上在去年就用上了 coreboot,只是當時只折騰到了能跑起來就沒繼續了。前兩天看到萌狼的文章就又想折騰了,在大佬的指點和我的摸索下,總算是折騰到了比較滿意的一個程度。


以下是需要準備的東西:

  • ThinkPad X220 和另一臺電腦(這當然不用說了)
  • 編程器 & 編程夾,X220 的 Flash 芯片是 SOIC-8 封裝的(如果以前用的是原廠 BIOS 的話必須要用編程器才能刷寫;如果刷了 coreboot 並沒有鎖上刷寫的話可以不用)
  • 手,眼睛,腦子!!!

首先把編譯 coreboot 的依賴裝好,GCC 必然是不可缺少的,此外還有 GNAT(Ada 編譯器,其實也可以算是 GCC 的一部分,不過一般都是分開來打包的樣子)、bisonflexncursesmake menuconfig 時需要)、wgetzlib

如果你的發行版裏不提供其中的一個或一些包,你可以選擇自己編譯一個用或是 debootstrap 一個最小的 Debian 環境然後 chrootsystemd-nspawn 進去用。我的 X220 上跑的是 AOSC OS 而在我寫這篇文章的時候源裏還沒有 GNAT,我就是這麼解決的。

然後把 coreboot 的源碼拖下來:

$ git clone https://review.coreboot.org/coreboot.git
$ cd coreboot && git submodule update --init --checkout

即使發行版提供 flashrom 這個刷寫工具也還要把 flashrom 的源碼拖下來,因爲等會兒要用到源碼裏的一個工具:

$ git clone https://review.coreboot.org/flashrom.git

進入 coreboot 的源碼目錄裏,準備好工具鏈等等:

$ make crossgcc-i386 CPUS=4 # 這裏只需要給 X220 用,所以準備 i386 的工具鏈就足夠了
$ make iasl # 準備 iASL 編譯器

接下來就需要對硬件開刀了,ThinkPad X220 可以說是挺好拆的,後面幾個標着鍵盤和掌托的螺絲擰下來就能很輕鬆得拆下鍵盤和掌托,Flash 芯片就在掌托那塊地方的左下角黑色貼紙下面。用編程夾夾上去,再把線接到編程器或者支持 SPI 的板子上啥的就行了,這方面我就不多說了,相關的資料很多。

Flashchip

(圖片來自 coreboot Wiki: https://www.coreboot.org/File:X220_flashchip.jpg

flashrom 把現有的固件 Dump 出來:

$ sudo flashrom -p internal:laptop=force_I_want_a_brick -c MX25L6405D -r dump.rom # 這裏舉例用的是 internal flash 的命令,如果用編程器的話需要根據編程器進行修改。

可能需要多做幾次然後比對下 hash,這是爲了避免讀出差錯。

這之後先別急,現階段最重要的事情是:

備份!
備份!!
備份!!!

(XD)

修改 BIOS 畢竟還是有一定風險的,如果編譯出來的 coreboot 有問題輕則無法啓動變成大號板磚,重則主板燒毀客服開不一定給你修(畢竟這機器都多少年了),但是如果有備份而且硬件沒掛的話就能用編程器再救回來。

進入 coreboot 源碼目錄的 util/ifdtool 目錄下並編譯它:

$ cd util/ifdtool && make

然後使用 ifdtool 解鎖固件描述(firmware descriptor,不知道怎麼叫纔是正確的)和 ME 區域。

$ ifdtool -u dump.rom # 把文件名改成自己的

ifdtool 會創建一個新的 dump.rom.new,接下來的步驟要用它。

如果只是想幹掉 ME 的話現在拿去過一邊 me_cleaner 應該就可以刷回去了。

$ me_claner.py dump.rom.new # 換成你自己的文件名

(我也不知道 coreboot 的構建系統雖然有相關選項但是會不會真的去淦 ME,所以在這裏推薦也國一下 me_cleaner)。

進入 flashrom 源碼目錄下的 util/ich_descriptors_tool 目錄並編譯 ich_descriptors_tool:

$ cd util/ich_descriptors_tool && make

然後用它來提取文件:

$ ich_descriptors_tool -f dump.rom.new -d # 當然,還是要改成自己的文件名

這個工具會輸出一大堆東西,不用管它,我們只要它創建的 dump.rom.new.Descriptor.bindump.rom.new.ME.bindump.rom.new.GBE.bin,分別代表着需要用到的三大坨 blob。

然後我們可以把這些文件複製到 coreboot 源碼目錄的 3rdparty/blobs 目錄的對應位置裏(這是爲了之後配置起來方便點)。

$ cp dump.rom.new.Descriptor.bin ~/coreboot/3rdparty/blobs/mainboard/lenovo/x220/descriptor.bin
$ cp dump.rom.new.ME.bin ~/coreboot/3rdparty/blobs/mainboard/lenovo/x220/me.bin
$ cp dump.rom.new.GBE.bin ~/coreboot/3rdparty/blobs/mainboard/lenovo/x220/gbe.bin

這裏可能會有人要問爲什麼不用提取 VGA BIOS,這個我起先也懷疑過,自己也提取了試過,對比之後發現 native init 和用 VGA BIOS 的效果實際上差不多甚至更好(因爲能用高分辨率模式),而且還是開源的實現,能少一坨 blob 就少一坨吧(

接下來就是配置 coreboot 了。


回到 coreboot 的源碼目錄下執行 make menuconfig 開始配置。

在這裏跟配置 Linux 時是一樣的操作方式,上下移動選擇項目,左右移動要進行的操作,? 查看幫助,/ 搜索。

Config-1

首先進入第一個 General setup,我個人的配置是這樣的:

()  Local version string
(fallback) CBFS prefix to use
    Compiler to use (GCC)  ---> # 一般還是用 GCC 吧,LLVM/Clang 支持還在測試的樣子。
[ ] Allow building with any toolchain
[ ] Use ccache to speed up (re)compilation # 雖說 ccache 能加快重複編譯的速度,但是一般也用不到... 反正編譯一下挺快的
[*] Generate flashmap descriptor parser using flex and bison
[*] Generate SCONFIG & BLOBTOOL parser using flex and bison
[*] Use CMOS for configuration values
[ ]   Load default configuration values into CMOS on each boot
[*] Compress ramstage with LZMA
[*] Include the coreboot .config file into the ROM image
[*] Create a table of timestamps collected during boot
[*] Allow use of binary-only repository # 這條一定要打開
[ ] Code coverage support
[ ] Undefined behavior sanitizer support
-*- Build the ramstage to be relocatable in 32-bit address space.
[ ] Update existing coreboot.rom image
[ ] Add a bootsplash image # 我至今沒搞明白這個怎麼用,與其加一張圖進去浪費寶貴的閃存空間,不如就讓它空着,反正 coreboot 初始化挺快的。

之後是 Mainboard,這邊的配置在選好了主板型號之後基本上就不用動了:

    *** Important: Run 'make distclean' before switching boards ***                                                                                                           
    Mainboard vendor (Lenovo)  ---> # 選聯想
    Mainboard model (ThinkPad X220)  ---> # 當然是 X220 啦
    ROM chip size (8192 KB (8 MB))  ---> # 除非你魔改過,不然不用改
(0x100000) Size of CBFS filesystem in ROM
()  fmap description file in fmd format

然後 Chipset

    *** SoC ***
    *** CPU ***
[*] Enable VMX for virtualization
[*]   Set lock bit after configuring VMX
    Include CPU microcode in CBFS (Generate from tree)  --->
()  Microcode binary path and filename
    *** Northbridge ***
-*- Use native raminit
[ ]   Ignore vendor programmed fuses that limit max. DRAM frequency
[ ]   Ignore XMP profile max DIMMs per channel
*** Southbridge ***
Flash locking during chipset lockdown (Don't lock flash sections)  --->
[ ] Lock down chipset in coreboot
    *** Super I/O ***
    *** Embedded Controllers ***
[*] Beep on fatal error
[*] Flash LEDs on fatal error
[*] Support bluetooth on wifi cards
    *** Intel Firmware ***
[*] Add Intel descriptor.bin file # 必選
(3rdparty/blobs/mainboard/$(MAINBOARDDIR)/descriptor.bin) Path and filename of the descriptor.bin file # 選擇剛剛複製的 descriptor.bin
[ ]   Configure IFD for EM100 usage
[*]   Add Intel ME/TXE firmware # 必選
(3rdparty/blobs/mainboard/$(MAINBOARDDIR)/me.bin) Path to management engine firmware # 同上
[*]     Verify the integrity of the supplied ME/TXE firmware
[*]     Strip down the Intel ME/TXE firmware # 如果要幹 ME 的話選上這個
          *** Please test the modified ME/TXE firmware and coreboot in two steps ***
[*]   Add gigabit ethernet firmware # 必選
(3rdparty/blobs/mainboard/$(MAINBOARDDIR)/gbe.bin) Path to gigabit ethernet firmware # 同上
[ ]   Add EC firmware
[ ] Lock ME/TXE sectionBootblock behaviour (Always load fallback)  --->

Devices 裏:

    Graphics initialization (Use native graphics init)  ---> # 這就是之前提到的 native init
    Display  --->
        Framebuffer mode (Linear "high-resolution" framebuffer)  ---> # 如果等會兒選擇用 GRUB2 作爲 payload,那麼更推薦用這個 high-resolution 的,如果用 SeaBIOS 的話更推薦用 Legacy VGA text mode,不然可能 SeaBIOS 的輸出可能只佔屏幕的四分之一
-*- Enable PCIe Common Clock
-*- Enable PCIe ASPM
[*] Enable PCIe Clock Power Management
[*] Enable PCIe ASPM L1 SubState
[ ] Early PCI bridge
(0x0000) Override PCI Subsystem Vendor ID
(0x0000) Override PCI Subsystem Device ID
[ ] Add a VGA BIOS image # 如果用了 native init 就不需要選擇
[ ] Add a Video Bios Table (VBT) binary to CBFS
[*] Enable I2C controller emulation in software

Generic Drivers 裏基本不需要動:

[ ] AS3722 RTC support
[*] SPI flash driver support in SMM
[ ] Disable Fast Read command
[ ] Serial port on SuperIO
[ ] Oxford OXPCIe952
(0x0) UART's PCI bus, device, function address
[ ] USB 2.0 EHCI debug dongle support
[*] Support Intel PCI-e WiFi adapters
[*] PS/2 keyboard init # 必選
[*] Enable TPM support # 如果需要 TPM 的話可以選上
[ ]   Deactivate TPM
[ ] Silicon Image SIL3114
[ ] TI TPS65913 support
[ ] TI TPS65913 RTC support

Security 下沒需求的話就不用改了:

    Verified Boot (vboot)  --->
        [ ] Verify firmware with vboot.

Console 裏:

[*] Squelch AP CPUs from early console.
[ ] spkmodem (console on speaker) console output
[ ] Use onboard VGA as primary video device
[ ] Network console over NE2000 compatible Ethernet adapter
[ ] Send console output to a CBMEM buffer
[ ] SPI Flash console output
    Default console log level (8: SPEW)  --->
[ ] Don't show any POST codes
[ ]   Store post codes in CMOS for debugging
[ ]   Show POST codes on the debug console
[ ]   Send POST codes to an external device
[*]   Send POST codes to an IO port
(0x80)  IO port for POST codes

System Table

[*] Generate SMBIOS tables

Payload 裏比較特殊,需要根據自己的需求選擇 _(:з」∠)_。
現在官方支持的 Payload 有:

  • None - 無,什麼都沒有。
  • An ELF executable payload - 一個 ELF 可執行文件作爲 Payload。
  • Bayou - 一個可以從 CBFS(coreboot filesystem)裏選擇並啓動別的 payload 的工具(是不是有點繞)。
  • FILO - 一個啓動管理器,比較簡單的那種。
  • GRUB2 - 知名啓動管理器。
  • SeaBIOS - 一個開源的 BIOS 實現,爲默認選項。
  • U-Boot - 熟悉 ARM 的朋友應該知道(跑
  • A Linux payload - 直接把一個 Linux 內核作爲 payload 塞進去。
  • Tianocore coreboot payload package - 開源的 UEFI 實現。

其中第一個肯定是不行的,coreboot 只執行一些基本的初始化,操作系統引導等等還是要交給 payload 來做的;
第二個可以在確認 coreboot 能用後做自定義時使用,比如我現在就是先生成好 GRUB2 的 elf 文件然後再把它編譯進 coreboot;
Bayou 沒用過,不過看起來很雞肋的樣子…;
FILO 也沒用過,不過都有 GRUB2 了還用啥 FILO;
GRUB2 是一個 OS,不解釋 _(:з」∠)_;
U-Boot 現在支持還是試驗階段;
直接用內核的只要你能把內核裁剪到夠小,那就用吧(x;
Tianocore 的話嘛… 東西是好東西,但是那個構建系統感覺有點可怕,我對它有心理陰影 Orz… 而且 X220 本身對 EFI 的支持了不算很好,不算很推薦(當然如果你認爲 EFI 是剛需的話可以用它。

不同的 Payload 配置方法不一樣。在這裏就不多說了,如果第一次用的話個人建議先用 SeaBIOS,等後來熟悉並且確認能用了再換 GRUB2 等等,畢竟 GRUB2 需要自己寫配置 Orz

Debugging 底下什麼都不用選(

全部配置好之後保存然後退出。

使用 make 就能打開新世界大門了。

編譯好的固件在源碼目錄的 build/coreboot.rom


進入 build 目錄下就可以用 flashrom 刷了。

$ sudo flashrom -p internal:laptop=force_I_want_a_brick -c MX25L6405D -w coreboot.rom # 同樣的,這裏的命令也是 internal flash 的命令,如果用編程器的話需要根據情況進行修改。

如果能正常啓動並且看到 payload 的界面,那就大功告成了。

之前備份的原廠 BIOS 千萬不要刪除。


以下是關於如何配置 GRUB2 的。

GRUB2 需要一個配置文件才能正常工作,而這個配置文件相關的信息挺難找的,我是在 BLUG 的 persmule.y 大佬的指點下才配成了現在的樣子。

首先要明確一件事情,如果用 GRUB2 作爲 payload,那麼它的配置文件在編寫的時候必定要考慮到是否足夠靈活,不然每次有一點點小變化就要重新刷固件那還不如直接用 SeaBIOS。

詳細情況可以參考 https://github.com/hardenedlinux/Debian-GNU-Linux-Profiles/blob/master/docs/hardened_boot/grub-for-coreboot.md

基本上直接參考上面就能夠勝任絕大多數情況了。

以下是個人認爲的幾個注意點:

  • 一些經常變動的東西可以寫到硬盤上讓 GRUB 自己去加載,而不是寫到固件裏面。
  • (memdisk)(cbfsdisk) 要區分清楚,(cbfsdisk) 是 ROM 裏的 CBFS 區域,而 (memdisk)grub-mkstandalone 時包括進去的部分。
  • 如果圖形相關選擇的是 native init 的 high-resolution framebuffer,並且進去後發現只有四分之一個屏幕可用的話可以試着在配置裏加上 gfxpayload=keepterminal_output --append gfxterm,應該可以讓它恢復正常。

另外有什麼的話,想到再寫 XD