# 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。這種做法既醜陋又帶著歷史包袱，大家覺得這不是長久之計。

那時候有多個候選方案：

1. Open Firmware： 原本 Sun、Apple PowerPC 常用的基於 Forth 語言的開機標準。
2. ARC（Advanced RISC Computing）： 早期 MIPS 架構或 Alpha 處理器用過的精簡指令集開機規範
3. 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 有分成

1. FV Recovery: 最底層 CPU 初始化、記憶體訓練、韌體更新/復原邏輯
2. FV Main: 幾乎所有的裝置驅動、開機選單、OS 載入器介面

![alt text](image.png)

整張圖將開機流程分成由下往上的三層安全架構：

1. 硬體層（藍色）： CPU、晶片組（NB/SB）、記憶體，以及掛在 LPC 匯流排上的 TPM（可信賴平台模組）
2. S-CRTM（綠色，靜態信任度量根）
   1. 系統一通電，從唯讀或受保護的 FV Recovery（復原韌體磁區） 執行 SEC 與 PEI 基礎架構。
   2. 安全更新驗證： 檢查更新包的數位簽章（Signed update/content、PhysPresence、SHA1），確保只有原廠簽核的程式碼能被寫入。
   3. 向上度量（Measure FV Main）： 在交棒給下一個階段之前，PEI 會先對主要韌體區塊（FV Main）計算雜湊值，並送入 TPM 進行度量存證。
3. 開機與環境驗證（DXE / BDS - FV Main）
   1. 進入主要韌體區塊（黃色）： 執行環境包括 DXE 與 BDS 開機階段。
   2. UEFI TCG Measurement： 根據 TCG 規範，將每個即將載入的驅動、執行檔雜湊值記錄到記憶體中（Measurement Log in ACPI Memory），形成開機稽核紀錄。
   3. UEFI Secure Boot（安全開機）：檢查即將執行的作業系統載入器（UEFI-OS Ldr And Drivers）是否具備合法的數位簽章（圖中的綠色鑰匙符號 Signed Loader）。

## CH2

UEFI 的六大概念

1. 物件（Objects managed by UEFI-based firmware）： 韌體用來管理系統狀態的機制，包含 I/O 裝置、記憶體與事件。
2. UEFI 系統表（The UEFI System Table）： 最核心的資料結構，內含各種資料資訊表以及能與系統互動的函式呼叫（Function calls）。
3. 代辦資料庫與協議（Handle database and protocols）： 讓各種可呼叫介面得以註冊與被找到的方法。
4. UEFI 映像檔（UEFI images）： 部署程式碼的實際可執行內容格式。
5. 事件（Events）： 讓軟體能夠在接收到其他活動訊號時被觸發的機制。
6. 裝置路徑（Device paths）： 用來描述硬體實體在系統中確切位置的資料結構，例如格式化磁碟上的匯流排、磁碟機、分割區與 UEFI 映像檔檔名。

### System Table

它是 UEFI 中最重要的資料結構，當任何驅動程式或應用程式執行時，都會在其進入點（Entry-point）收到一個指向此系統表的指標，藉此取得系統配置資訊與豐富的服務。

三大核心服務：

1. UEFI Boot Services（開機服務）：透過 Boot Services Table 進行存取。
2. UEFI Runtime Services（執行時期服務）：透過 Runtime Services Table 存取；各版本所提供的服務數量與類型都是固定的（依循 UEFI 2.6 規格）。
3. Protocol services（協議服務）：一組由 16 位元 GUID（全域唯一識別碼） 命名的相關函式與資料欄位。

Protocol 通常用於為裝置（如主控台、磁碟、網路）提供軟體抽象化，也能用來擴充平台的通用服務。

### Handle Database

它是 UEFI 韌體用來維護各種物件的中央儲存庫，資訊是全域的，任何可執行的 UEFI 映像檔都可以進行存取。裡面包含由 Handles（代辦/句柄） 與 Protocols（協議） 組成的物件。

一個 Handle 本質上是一個或多個 Protocol 的集合。Handle 可以代表的元件：

1. 可執行映像檔：例如 UEFI 驅動程式與應用程式。
2. 實體或邏輯裝置：例如網路控制器與硬碟分割區。
3. UEFI 系統服務：例如 UEFI 解壓縮服務（Decompression）或 EBC 虛擬機。

就像 Linux 的「一切皆檔案（Everything is a file）」透過檔案介面來統一抽象化管理硬體、行程與資料一樣，UEFI 也有著類似的核心設計哲學：在 UEFI 的世界裡，系統中的各種資源都是透過「Handle（代辦/句柄）」與掛在其上的「Protocol（協議）」來進行管理與存取。

1. Agent Handles（代理代辦）：與「程式執行與驅動」有關的代辦。
   1. Image Handles（映像檔代辦）： 代表被載入記憶體執行的程式或驅動。
   2. Driver Handles（驅動程式代辦）： 代表具備驅動能力的模組。
   3. Driver Image Handles（交集區）： 既是映像檔、又是驅動程式的 Handle。
2. Controller Handles（控制器代辦）：與「硬體或邏輯裝置」有關的代辦。
   1. Physical Controller Handles（實體控制器代辦）： 代表真實的硬體裝置（例如網卡晶片、硬體控制器）。
   2. Virtual Controller Handles（虛擬控制器代辦）： 代表邏輯上或軟體模擬出來的控制器（例如硬碟分割區、虛擬裝置）。
3. Service Handles（服務代辦）：不屬於硬體也不屬於傳統程式，專門用來提供特定「系統服務」的代辦。

### Protocol

1. UEFI Driver（驅動程式）： 是一個可執行的映像檔，其主要任務是在各種 Handle 上安裝多種 Protocol 來完成工作。
2. UEFI Protocol（協議）： 是一組由規格定義的函式指標、資料結構或 API 區塊。

GUID 是真正的名稱： 每個 Protocol 都必須具備一個 16 位元的 GUID。像 LocateProtocol 這類的開機服務就是靠這個編號在 Handle Database 中尋找對應的協議。

僅限開機階段有效： 所有的 UEFI 程式只能在開機階段（Boot time）透過 Handle Database 操作協議。 失效時機： 一旦系統呼叫了 ExitBootServices() 將控制權移交給作業系統後，整個 Handle Database 就會關閉且無法再使用。

不同 Handle 可共用同種協議： 不同的 Handle 可以各自安裝同一個協議，但內部帶有不同的資料與行為。例如：

1. PCI I/O 協議： PCI 匯流排會為系統中的每個 PCI 裝置各安裝一個 PCI I/O 協議實例，各自記錄該裝置獨有的硬體位置與 Option ROM 大小。
2. 每個驅動程式的 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）。

三大映像檔類型：

1. UEFI 應用程式（Applications）： 執行完畢離開後，記憶體與狀態就會被回收（例如 Windows 或 Linux 的 OS 載入器，對 UEFI 來說本質上就是一種應用程式）。
2. UEFI 開機服務驅動（Boot Service Drivers）： 存在於整個預開機階段，直到作業系統呼叫 ExitBootServices() 後才釋放記憶體。
3. 執行時期驅動（Runtime Drivers）： 生命週期會持續到作業系統運行中，開機後依然存在並可被作業系統調用。

UEFI 映像檔不是死板地編譯在特定記憶體位址，而是帶有重定位修復資料（Relocation fix-ups），可以被放置在記憶體的任意空間。呼叫開機服務 gBS->LoadImage() 時會為映像檔配置記憶體，自動套用重定位修正。

映像檔被載入後，會透過 gBS->StartImage() 叫用其標頭中指定的進入點（Entry Point）。進入點固定會接收到兩個關鍵參數：

1. Image Handle（自己的代辦代號）
2. UEFI System Table 的指標

不同的 Imgae type

![alt text](image-1.png)

### Event

* 克服沒有中斷的限制： UEFI 本身不支援傳統的硬體中斷（僅支援單一計時器中斷），這對習慣中斷驅動的工程師是一大挑戰。
* 輪詢機制（Polled Drivers）： 為了彌補這個限制，UEFI 依賴事件機制，最常見的是透過「計時器事件」讓驅動程式定期去輪詢硬體狀態。
* 事件狀態： 事件可以被建立與銷毀，並在「等待中（Waiting）」與「已觸發（Signaled）」兩種狀態之間切換。

每個事件都綁定了：

* 工作優先級（TPL）： 執行通知函式時所在的優先層級。
* 通知函式（Notification function）： 當事件狀態改變或被等待時所要執行的程式碼。
* 通知上下文（Notification context）： 每次執行通知函式時會一併帶入的參數資料。

TPL 的概念類似微軟 Windows 的 IRQL 或 Unix 的 SPL，用來規範存取控制的優先順序：

1. TPL_APPLICATION： 一般 UEFI 應用程式執行的優先級。
2. TPL_CALLBACK： 多數通知函式使用的優先級。
3. TPL_NOTIFY： 多數 I/O 操作執行的優先級。
4. TPL_HIGH_LEVEL： UEFI 唯一支援的計時器中斷專用層級。

## CH3 UEFI Driver Model，UEFI 驅動程式模型）

UEFI 驅動模型旨在簡化裝置驅動的設計與實作，並產生較小的執行檔體積。為了達到這點，它把一部分複雜度轉移到了匯流排驅動（Bus drivers）與共用韌體服務身上。

1. 載入階段 (LoadImage)
   1. 驅動程式檔案可以從 ROM、Flash、硬碟或網路等各種媒體中被找到。
   2. 透過開機服務 LoadImage() 將 PE/COFF 格式的映像檔載入記憶體中，系統會為它建立一個 Handle，並在其上面安裝 Loaded Image Protocol（此時稱為 Image Handle）。此時驅動程式只是安靜地躺在記憶體裡，還沒開始執行。
2. 有的 UEFI 應用程式與驅動程式，載入後都必須透過開機服務 StartImage() 來正式啟動。
3. 轉變成 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： 探討企業級伺服器或系統的遠端管理與可管理性協定。

