2024年3月22日 星期五

查看、修改與保護 uefi 裡面的 nvram/變數

安裝最新的 0.14 版 refind 套件時, 畫面閃過 "Creating new NVRAM entry" 的訊息, 下次開機時發現它竟然直接攻佔了我電腦的開機選單! 而且它不是安裝在某顆硬碟的 MBR, 而是裝在更上游的 UEFI NVRAM, 也就是主機板的韌體上。 還好 linux 程式都很有分寸的 , 只是多加了一層選單, 從那裡仍舊可以看到並選取我原來的選單。 (而不是像三十幾年前, 沒品的微軟直接廢了我原本的 OS/2 多重開機選單。) 總之這促使我開始爬文認識 UEFI 韌體管理/EFI variables。 以下測試不必進入 UEFI, 在 linux 命令列底下就可以進行。

近年的主機板上的 UEFI 韌體, 它的一些設定 (例如優先從哪個裝置開機?) 儲存在 nvram 裡面, 就像古代的 BIOS 把設定資訊存在 CMOS 裡面一樣。 在 linux 底下, ls /sys/firmware/efi/efivars 會列出 nvram 裡面的所有變數名稱, 名稱結構是 「變數簡稱-guid」。 但這些檔案的內容並不是單純的 ascii 或 utf8 文字。 若要進一步觀察甚至修改這些變數, 就要安裝兩個套件: apt install efivar efibootmgr 以下有些動作可能需要 root 的權限。

首先, efivar -l 一樣可以列出 nvram 裡面的所有變數名稱, 跟上述 ls 列出的一樣, 只是名稱結構正好顛倒: 「guid-變數簡稱」。 下面兩個指令可以驗證上述觀察: ls /sys/firmware/efi/efivars | perl -pe 's/(\w+)-(.{36})$/$2-$1/' | sort > /tmp/efivars.txt ; efivar -l | sort | diff - /tmp/efivars.txt

要查看某個變數的值用 -n 選項。 例如: efivar -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-Boot0000 那麼, 現在就趕快先把所有變數目前的值存起來:

mkdir efivar
cd efivar
for v in $(efivar -l) ; do efivar -n $v > $v.txt ; done

這些內容的格式看起來有點怪。 查了很久才知道: 根據 UEFI 規格, 很多時候字串都採用 UCS2 編碼; 有些則是二進位值, 例如 *-TimeOut。 別的變數暫時就先算了; 我只想知道其中 boot 相關變數該如何設定, 但卻查不太到解釋 efi boot variable format 的文章, 只找到一篇 (為什麼是來自 wikileaks 的?!) 很簡略文章 NVRAM Variables Explained 以及一篇 開發者的部落格文章

改搜尋 "efibootmgr tutorial"。 從這兩篇: archlinuxlinux config 可以學到很多 efibootmgr 的指令, 例如: 用 efibootmgr --create ... 新增 boot entry、 用 efibootmgr -o 0010,0000... 更改各儲存裝置的開機優先順序、 用 efibootmgr --timeout=... 更改 UEFI 開機選單等待時間... 等等。

我只想把 UEFI 還原成出廠狀態。 用 efibootmgr | grep BootOrder 查出目前最優先的開機選項是 0000, 再用 efibootmgr | grep Boot0000 查出這個選項確實已被 refind 佔領, 於是用 efibootmgr --delete-bootnum --bootnum 0 把它刪掉。 下次再開機時, 第一手主控權又回到我硬碟上原先的 extlinux 手上了!

最後, 趕快按照 archlinux 的教學, 在 fstab 裡面加一句:
efivarfs /sys/firmware/efi/efivars efivarfs ro,nosuid,nodev,noexec 0 0
以便把 /sys/firmware/efi/efivars 目錄改成唯讀 (下次開機才會生效), 以後若再遇到任何軟體 (例如 支援 UEFI 光碟的開機載入程式 rEFInd) 企圖去改 nvram, kernel 就會把它擋下來。 自己確定真的想寫入時, 可以再用 mount -o remount,rw efivarfs /sys/firmware/efi/efivars 暫時開放寫入權限。 用 mount | grep efi 可以查詢目前它是唯讀 (ro) 或可讀寫 (rw) 的。

我認為 linux 系統應該預設把這個目錄掛載成唯讀的, 以免不小心下個 rm 指令或 output redirection 到錯誤的地方, 就毀了 UEFI。 有人 向 systemd 提出建議, 但很遺憾, 被拒絕。 當然, 問題的源頭在於 UEFI 韌體; 也許幫韌體擦屁股並不是 systemd 的責任, 而應該是 kernel? 或是各家 distributions? 我也不知道。 但總之, 這個未解的問題引起了很多 討論

沒有留言:

張貼留言

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。