Beyond BIOS 書籍筆記
CH1
UEFI 是一套純粹的介面規格(Interface Specification),規定了外部程式如何與韌體互動,但它完全不干涉平台韌體內部是如何被建立或撰寫的;韌體內部的具體實作方式是由 PI(Platform Initialization)規格 來定義的。
所有需要跟這套介面溝通的軟體都是它的使用者,包括:作業系統載入器(OS Bootloader)、系統安裝程式、開機裝置的擴充 ROM(Option ROM)、開機前的硬體診斷工具、公用程式,以及開機完成後少數會用到的作業系統執行時期服務(UEFI Runtime Services)。
UEFI 最關鍵的任務就是「開機」——負責把系統控制權穩妥地交棒給下一層軟體(通常是作業系統的 Bootloader)。
UEFI 本身具備強大的能力,甚至可以不載入 Windows、Linux、macOS 等完整的多工作業系統,直接在 UEFI 環境下執行一些獨立的小工具或程式;但這只是它的附加價值,並非它的主要設計初衷。
UEFI 框架採用 PI 架構把韌體拆分成許多獨立模組(如 PEIM、DXE 驅動程式),需要透過 GUID 發布 API、搜尋並動態調度程式碼,這會消耗寶貴的 SPI ROM 儲存空間,也會拖慢開機時間(Boot Time)。PI 的模組化架構雖然有少許性能開銷,卻是解決跨廠商合作、加速產品上市時間(Time-to-Market)的唯一商業解法。
- EDK(EFI Development Kit): 早期在 TianoCore 開源的參考實作專案,實作了基礎的 UEFI 與舊 Framework 規範,但它不是一套開箱即用的完整商業版 BIOS
- UDK(UEFI Development Kit): EDK 的第二代演進產物(基於 EDK II),加入許多架構增強與程式碼管理能力(例如書中提及的 UDK2015 即代表該年份的正式穩定發行版本)
歷史
1990 年代末 Intel 正在研發劃時代的 64 位元伺服器架構 Itanium,舊有的 PC 開機方式是傳統 BIOS,依賴極度古老的 16 位元軟體中斷(如磁碟存取的 INT 13h)。
最早有人提案用 SAL(System Architectural Layer) 的 SAL_PROC 介面,簡單說就是「把舊 PC BIOS 的中斷呼叫包裝成函式參數」,比如用 SAL_PROC(0x13, …) 來模擬 INT 13h。這種做法既醜陋又帶著歷史包袱,大家覺得這不是長久之計。
那時候有多個候選方案:
- Open Firmware: 原本 Sun、Apple PowerPC 常用的基於 Forth 語言的開機標準。
- ARC(Advanced RISC Computing): 早期 MIPS 架構或 Alpha 處理器用過的精簡指令集開機規範
- Intel EFI(Extensible Firmware Interface): Intel 自行開發的方案,因為具備良好的架構無關性(Architecture-neutral),最終脫穎而出。Intel 在 1999 年正式釋出初版 EFI 規範,同時支援 Itanium 和一般的 32 位元 x86(IA-32)。
EFI 從 1.02 升級到 EFI 1.10,其中最關鍵的突破是引入了 EFI 驅動程式模型(EFI Driver Model)。這讓開機階段的裝置驅動不再是一團混亂的組合語言,而是有統一生命週期、支援熱插拔與匯流排拓撲的現代化驅動架構。
2005 年發布的 UEFI 2.0 大致繼承自 EFI,但補上了兩大關鍵升級:加入了支援 IPv4 的模組化網路協定堆疊(Networking Stack APIs),為 PXE 網路開機與遠端部署奠定基礎。以及正式支援 x64 架構。
安全性
先前知識,一般 SPI Flash 有分成
- FV Recovery: 最底層 CPU 初始化、記憶體訓練、韌體更新/復原邏輯
- FV Main: 幾乎所有的裝置驅動、開機選單、OS 載入器介面

整張圖將開機流程分成由下往上的三層安全架構:
- 硬體層(藍色): CPU、晶片組(NB/SB)、記憶體,以及掛在 LPC 匯流排上的 TPM(可信賴平台模組)
- S-CRTM(綠色,靜態信任度量根)
- 系統一通電,從唯讀或受保護的 FV Recovery(復原韌體磁區) 執行 SEC 與 PEI 基礎架構。
- 安全更新驗證: 檢查更新包的數位簽章(Signed update/content、PhysPresence、SHA1),確保只有原廠簽核的程式碼能被寫入。
- 向上度量(Measure FV Main): 在交棒給下一個階段之前,PEI 會先對主要韌體區塊(FV Main)計算雜湊值,並送入 TPM 進行度量存證。
- 開機與環境驗證(DXE / BDS - FV Main)
- 進入主要韌體區塊(黃色): 執行環境包括 DXE 與 BDS 開機階段。
- UEFI TCG Measurement: 根據 TCG 規範,將每個即將載入的驅動、執行檔雜湊值記錄到記憶體中(Measurement Log in ACPI Memory),形成開機稽核紀錄。
- UEFI Secure Boot(安全開機):檢查即將執行的作業系統載入器(UEFI-OS Ldr And Drivers)是否具備合法的數位簽章(圖中的綠色鑰匙符號 Signed Loader)。
CH2
UEFI 的六大概念
- 物件(Objects managed by UEFI-based firmware): 韌體用來管理系統狀態的機制,包含 I/O 裝置、記憶體與事件。
- UEFI 系統表(The UEFI System Table): 最核心的資料結構,內含各種資料資訊表以及能與系統互動的函式呼叫(Function calls)。
- 代辦資料庫與協議(Handle database and protocols): 讓各種可呼叫介面得以註冊與被找到的方法。
- UEFI 映像檔(UEFI images): 部署程式碼的實際可執行內容格式。
- 事件(Events): 讓軟體能夠在接收到其他活動訊號時被觸發的機制。
- 裝置路徑(Device paths): 用來描述硬體實體在系統中確切位置的資料結構,例如格式化磁碟上的匯流排、磁碟機、分割區與 UEFI 映像檔檔名。
System Table
它是 UEFI 中最重要的資料結構,當任何驅動程式或應用程式執行時,都會在其進入點(Entry-point)收到一個指向此系統表的指標,藉此取得系統配置資訊與豐富的服務。
三大核心服務:
- UEFI Boot Services(開機服務):透過 Boot Services Table 進行存取。
- UEFI Runtime Services(執行時期服務):透過 Runtime Services Table 存取;各版本所提供的服務數量與類型都是固定的(依循 UEFI 2.6 規格)。
- Protocol services(協議服務):一組由 16 位元 GUID(全域唯一識別碼) 命名的相關函式與資料欄位。
Protocol 通常用於為裝置(如主控台、磁碟、網路)提供軟體抽象化,也能用來擴充平台的通用服務。
Handle Database
它是 UEFI 韌體用來維護各種物件的中央儲存庫,資訊是全域的,任何可執行的 UEFI 映像檔都可以進行存取。裡面包含由 Handles(代辦/句柄) 與 Protocols(協議) 組成的物件。
一個 Handle 本質上是一個或多個 Protocol 的集合。Handle 可以代表的元件:
- 可執行映像檔:例如 UEFI 驅動程式與應用程式。
- 實體或邏輯裝置:例如網路控制器與硬碟分割區。
- UEFI 系統服務:例如 UEFI 解壓縮服務(Decompression)或 EBC 虛擬機。
就像 Linux 的「一切皆檔案(Everything is a file)」透過檔案介面來統一抽象化管理硬體、行程與資料一樣,UEFI 也有著類似的核心設計哲學:在 UEFI 的世界裡,系統中的各種資源都是透過「Handle(代辦/句柄)」與掛在其上的「Protocol(協議)」來進行管理與存取。
- Agent Handles(代理代辦):與「程式執行與驅動」有關的代辦。
- Image Handles(映像檔代辦): 代表被載入記憶體執行的程式或驅動。
- Driver Handles(驅動程式代辦): 代表具備驅動能力的模組。
- Driver Image Handles(交集區): 既是映像檔、又是驅動程式的 Handle。
- Controller Handles(控制器代辦):與「硬體或邏輯裝置」有關的代辦。
- Physical Controller Handles(實體控制器代辦): 代表真實的硬體裝置(例如網卡晶片、硬體控制器)。
- Virtual Controller Handles(虛擬控制器代辦): 代表邏輯上或軟體模擬出來的控制器(例如硬碟分割區、虛擬裝置)。
- Service Handles(服務代辦):不屬於硬體也不屬於傳統程式,專門用來提供特定「系統服務」的代辦。
Protocol
- UEFI Driver(驅動程式): 是一個可執行的映像檔,其主要任務是在各種 Handle 上安裝多種 Protocol 來完成工作。
- UEFI Protocol(協議): 是一組由規格定義的函式指標、資料結構或 API 區塊。
GUID 是真正的名稱: 每個 Protocol 都必須具備一個 16 位元的 GUID。像 LocateProtocol 這類的開機服務就是靠這個編號在 Handle Database 中尋找對應的協議。
僅限開機階段有效: 所有的 UEFI 程式只能在開機階段(Boot time)透過 Handle Database 操作協議。 失效時機: 一旦系統呼叫了 ExitBootServices() 將控制權移交給作業系統後,整個 Handle Database 就會關閉且無法再使用。
不同 Handle 可共用同種協議: 不同的 Handle 可以各自安裝同一個協議,但內部帶有不同的資料與行為。例如:
- PCI I/O 協議: PCI 匯流排會為系統中的每個 PCI 裝置各安裝一個 PCI I/O 協議實例,各自記錄該裝置獨有的硬體位置與 Option ROM 大小。
- 每個驅動程式的 Handle 都會安裝 EFI_COMPONENT_NAME2_PROTOCOL,但各自回傳專屬的名字(例如 USB 驅動回傳「USB bus driver」,PXE 驅動回傳「PXE base code driver」)。
Tag GUID: 協議不一定都要包含複雜的函式或資料,有些協議完全沒有內容,只剩下一個 GUID,這被稱為 Tag GUID,主要用來幫特定的 Handle 貼上「標籤」。例如 EDK II 會用 HOT_PLUG_DEVICE_GUID 來標記所有屬於熱插拔匯流排(如 USB)的裝置 Handle,讓其他 UEFI 程式能透過這個 GUID 快速把這些特殊裝置找出來。
UEFI Image
所有 UEFI 映像檔都採用微軟的 PE/COFF 標頭格式,支援 IA-32、x64、Itanium、ARM,以及與處理器無關的通用虛擬機指令(EBC)。
三大映像檔類型:
- UEFI 應用程式(Applications): 執行完畢離開後,記憶體與狀態就會被回收(例如 Windows 或 Linux 的 OS 載入器,對 UEFI 來說本質上就是一種應用程式)。
- UEFI 開機服務驅動(Boot Service Drivers): 存在於整個預開機階段,直到作業系統呼叫 ExitBootServices() 後才釋放記憶體。
- 執行時期驅動(Runtime Drivers): 生命週期會持續到作業系統運行中,開機後依然存在並可被作業系統調用。
UEFI 映像檔不是死板地編譯在特定記憶體位址,而是帶有重定位修復資料(Relocation fix-ups),可以被放置在記憶體的任意空間。呼叫開機服務 gBS->LoadImage() 時會為映像檔配置記憶體,自動套用重定位修正。
映像檔被載入後,會透過 gBS->StartImage() 叫用其標頭中指定的進入點(Entry Point)。進入點固定會接收到兩個關鍵參數:
- Image Handle(自己的代辦代號)
- UEFI System Table 的指標
不同的 Imgae type

Event
- 克服沒有中斷的限制: UEFI 本身不支援傳統的硬體中斷(僅支援單一計時器中斷),這對習慣中斷驅動的工程師是一大挑戰。
- 輪詢機制(Polled Drivers): 為了彌補這個限制,UEFI 依賴事件機制,最常見的是透過「計時器事件」讓驅動程式定期去輪詢硬體狀態。
- 事件狀態: 事件可以被建立與銷毀,並在「等待中(Waiting)」與「已觸發(Signaled)」兩種狀態之間切換。
每個事件都綁定了:
- 工作優先級(TPL): 執行通知函式時所在的優先層級。
- 通知函式(Notification function): 當事件狀態改變或被等待時所要執行的程式碼。
- 通知上下文(Notification context): 每次執行通知函式時會一併帶入的參數資料。
TPL 的概念類似微軟 Windows 的 IRQL 或 Unix 的 SPL,用來規範存取控制的優先順序:
- TPL_APPLICATION: 一般 UEFI 應用程式執行的優先級。
- TPL_CALLBACK: 多數通知函式使用的優先級。
- TPL_NOTIFY: 多數 I/O 操作執行的優先級。
- TPL_HIGH_LEVEL: UEFI 唯一支援的計時器中斷專用層級。
CH3 UEFI Driver Model,UEFI 驅動程式模型)
UEFI 驅動模型旨在簡化裝置驅動的設計與實作,並產生較小的執行檔體積。為了達到這點,它把一部分複雜度轉移到了匯流排驅動(Bus drivers)與共用韌體服務身上。
- 載入階段 (LoadImage)
- 驅動程式檔案可以從 ROM、Flash、硬碟或網路等各種媒體中被找到。
- 透過開機服務 LoadImage() 將 PE/COFF 格式的映像檔載入記憶體中,系統會為它建立一個 Handle,並在其上面安裝 Loaded Image Protocol(此時稱為 Image Handle)。此時驅動程式只是安靜地躺在記憶體裡,還沒開始執行。
- 有的 UEFI 應用程式與驅動程式,載入後都必須透過開機服務 StartImage() 來正式啟動。
- 轉變成 Driver Image Handle,一旦 StartImage() 執行完畢,該 Image Handle 因為成功安裝了 Driver Binding Protocol,就會正式升級轉變為 Driver Image Handle(如文末清單與圖 3.2 所示,身上掛滿了各式管理與診斷協議)
Other
- Chapter 4 – Protocols YouShould Know: 介紹開發與除錯中最常使用、不可不知的關鍵 UEFI 協議。
- Chapter 5 – UEFI Runtime: 探討作業系統進入後仍持續運行的 Runtime 服務與管理。
- Chapter 7 – Different Types of Platforms: 說明伺服器、個人電腦與嵌入式等不同硬體平台對韌體的需求差異。
- Chapter 8 – DXE Basics: Core, Dispatching, and Drivers: 深入 DXE(驅動程式執行環境)的核心架構、依賴性調度與驅動載入機制。
- Chapter 9 – Some Common UEFI and PI Functions: 列舉並說明常見的 UEFI 與 PI 內部常用函式。
- Chapter 10 – Platform Security and Trust: 探討平台安全架構,包含 Secure Boot(安全開機)、Measured Boot 與 TPM 信任鏈。
- Chapter 11 – Boot Device Selection: 介紹 BDS 階段的開機選單、開機裝置排序與管理機制。
- Chapter 12 – Boot Flows: 梳理整個系統從按下電源到作業系統載入的完整開機流程與時序。
- Chapter 13 – Pre-EFI Initialization (PEI): 深入最底層的 PEI 階段,講解早期 CPU 與記憶體(DRAM)初始化過程。
- Chapter 14 – Putting It All Together—Firmware Emulation: 介紹如何在模擬環境中整合與測試整套韌體。
- Chapter 15 – Reducing Platform Boot Times: 探討如何優化與縮短開機時間(Fast Boot 技術)。
- Chapter 16 – Embedded Boot Solution: 針對嵌入式系統的輕量化開機解決方案。
- Chapter 17 – Manageability: 探討企業級伺服器或系統的遠端管理與可管理性協定。