# UEFI BIOS tutorial
這篇文章主要用來記錄學習 UEFI EDKII 的心路歷程,由於非該領域的專業,因此難免會有錯誤。
以下介紹提供在windows環境或是Linux環境中的安裝與操作方式
==如有發現錯誤,還請大家踴躍指正==
## Try EDK2 on Fedora 41 (Linux)
### 需要的安裝套件:
```clike=
# 這會安裝開發C/C++工具所需的套件集合
dnf group install -y development-tools
# 以下是執行edkII 所需要的套件
dnf install nasm libXext-devel \
libX11-devel uuid-devel \
gcc-c++ libuuid-devel acpica-tools
```
### 下載官方鏡像原始程式碼:
```shell=
$ mkdir UEFI; cd UEFI
UEFI$ git clone https://github.com/tianocore/edk2.git
UEFI$ cd edk2
UEFI/edk2$ git submodule update --init
```
### 編譯EDKII工具鍊:
```shell=
UEFI/edk2$ make -C BaseTools
UEFI/edk2$ source edksetup.sh
```
第一次執行 source edksetup.sh 會在 Conf目錄生成 tools_def.txt、target.txt、build_rule.txt 檔案,這是之後 build 指令會預設讀取的資訊
build 指令也是 edk2 用來編譯 package 的命令腳本,路徑會在剛剛編譯的 BaseTools 下:
```clike
edk2/BaseTools/BinWrappers/PosixLike/build
```
## Try EDK2 on Windows 11
Windows 環境下比較常使用的toolchain是visual studio C/C++。
### 需要的安裝套件:
與Linux不同,Linux通常根據不同的發行版提供不同的安裝管理工具 (ex: Fedora use dnf/yum, Debian use apt, OpenSUSE use zypper/YasT)。
Windows通常是可以下載一個執行檔案,並透過執行該執行檔案來安裝環境。
- 下載 Visual Studio,並勾選C/C++開發環境

- 下載 Python,並將其添加進PATH中
- 下載 Nasm(組譯器),IASL(ACPI 元件體系結構下載),同樣須將其添加至PATH中
- 與Linux環境預設會安裝git不同,Windows一般需要自行安裝git for windows
### 下載官方鏡像原始程式碼:
開啟PowerShell,並在其輸入以下命令
```
PS> git clone https://github.com/tianocore/edk2.git
PS> cd edk2
PS> git submodule update --init
PS> .\edksetup.bat Rebuild # 這邊可以看到,命令以反斜線形式表示路徑
```

其中會輸出重要的環境路徑,以及如下的編譯過程

build 命令有幾個常用的選項可供配置:
- -a TARGETARCH or --arch=TARGETARCH: 該選項用於指定目標的指令及架構,常見的有 IA32(X86)、X64(AMD64, X86_64)、ARM、RISCV64
- -p PLATFORMFILE or --platform=PLATFORMFILE: 建構由 DSC 檔案名稱參數指定的平台,覆蓋 target.txt 的 ACTIVE_PLATFORM 定義
- -m MODULEFILE or --module=MODULEFILE: 建構由 INF 檔案名稱參數指定的模組,如果未提供該選項,預設會編譯 package 中的所有 Module
- -t TOOLCHAIN or --tagname=TOOLCHAIN: 使用工具鏈標記名建構平台,覆蓋 target.txt 的 TOOL_CHAIN_TAG 定義,在 windows 環境中通常是 VS2015,Linux 中則使用 GCC
- -b BUILDTARGET or --buildtarget=BUILDTARGET: 使用 TARGET 建置平台,覆蓋 target.txt 的 TARGET 定義,常用的有 RELEASE 或是 DEBUG
在 edk2 Module 中,模擬器是常用的工具,編譯該工具的方式如下:
```shell=
# 在該命令中我們沒有使用 -m 指定要編譯的 Module
# 因此會將 EmulatorPkg 中的所有 Module 都編譯
UEFI/edk2$ build -p EmulatorPkg/EmulatorPkg.dsc -t GCC -a X64
```
編譯完成之後會在下命令的該目錄之下,出現一個 Build 目錄,這裡面會有以我們編譯的 Package 為名稱的目錄。
以剛剛編譯的模擬器工具為例,如下:
```shell=
UEFI/edk2/Build$ ls
EmulatorX64/
# 進入 EmulatorX64 目錄後我們可以看到 DEBUG_GCC 目錄
# 雖然我們沒有使用 -b 來指定目標,但預設會是DEBUG
```
要執行虛擬機只需執行 Host,他會在剛剛編譯的套件路徑之下中,以下是執行後的範例:

可以看到很多 efi 執行檔,我們隨意執行一個 HelloWorld.efi 後即可得到結果。
### OVMF 開放虛擬機器韌體
OVMF 是基於 EDKII 的韌體,可以在 qemu x86-64 虛擬機器下運作。這樣可以輕鬆地使用 UEFI 韌體進行調試和實驗
```shell=
# 在該命令中我們沒有使用 -m 指定要編譯的 Module
# 因此會將 OvmfPkg 中的所有 Module 都編譯
UEFI/edk2$ build -p OvmfPkg/OvmfPkgX64.dsc -t GCC -a X64 -b RELEASE
# 編完的檔案放在 Build/{Platform Name}/{TARGET}_{TOOL_CHAIN_TAG}/FV
UEFI/edk2/Build/OvmfX64/RELEASE_GCC/FV$ ls -alh OVMF*
-rw-r--r--. 1 alan alan 3.5M Jan 2 17:07 OVMF_CODE.fd
-rw-r--r--. 1 alan alan 4.0M Jan 2 17:07 OVMF.fd
-rw-r--r--. 1 alan alan 528K Jan 2 17:07 OVMF_VARS.fd
```
- OVMF 韌體(QEMU 的 UEFI 實作)分為兩個檔案:
- OVMF_CODE.fd 包含實際的 UEFI 韌體
- OVMF_VARS.fd 是用於模擬持久 NVRAM 儲存的「模板」
有 OVMF 後我們就可以使用 qemu 模擬器來執行。
```shell=
$ qemu-system-x86_64 -s -pflash OVMF.fd -hda fat:rw:<efi_file_loc>\
-net none -debugcon file:debug.log -global -isa-debugcon.iobase=0x402 -nographic
```
執行之後,debug 的相關訊息會輸出到 debug.log

#### 使用qemu搭配gdb進行除錯


### UEFI Module
EDK2 的最重要兩個套件: Module、Package
Package 可以簡單的理解為 edk2 目錄中以 Pkg 為後綴的目錄都是一個 Package,Package 是由一組 Module 以及 Platform 檔案(.dsc) 和 Package 聲明檔案(.dec) 所組成。
### <font color=#008000>DEC</font>
EDK II Package Declaration File. 該檔案定義有關 Package 中提供的內容的資訊。 EDK II Package 是類似內容的集合。
### <font color=#008000>DSC</font>
EDK II Platform Description File. 該檔案描述了要建置的 Modules、Libraries和 Components以及如何構建,以及定義連結 EDK II 模組時將使用的程式庫實例。
### <font color=#008000>FDF</font>
EDK II Flash definition file. 此檔案用於定義韌體映像、更新套件和 PCI 選項 ROM 的內容和二進位映像佈局。
### <font color=#008000>INF</font>
EDK II Module Information File. 該文件描述了模組的編寫方式。對於 EDK,此檔案描述了 Components 或 Libraries 的編寫方式以及提供一些基本的建置資訊。 該檔案類似 Makefile
- 檔案內容由 Block 來組成,有些Block式必須要提供的,有部分是可選的
- [Defines] - 定義了 Module 的屬性 - 必須要是第一個描述的 section
- INF_VERSION: 版本號
- BASE_NAME: 編譯完後的執行檔案的名稱,如果為 test,則編譯完後會叫 test.efi
- FILE_GUID: UUID,Linux 中可以使用 uuidgen 來生成
- ENTRY_POINT: 這個為 Module 的 main function,非常重要
- [Sources] - 要編譯的原始程式碼
- 要描述其位置,root 路徑就是 inf 檔案的所在路徑
- [Sources.$(Arch)]: 用來特別說明哪些檔案只能在某個 ISA 中被編譯
- $(Arch): 是可選用的項目,常見的有 X64,IA32,ARM,如果在編譯中使用 -a X64,則代表 [Sources]以及 [Sources.X64] 的內容會被編譯
- Toolchain: 除了 ISA 為可指定選項外,Toolchain也是可以選用的,常見的有 GCC,INTEL,這會用來表示編譯時使用的編譯器要選擇哪一個,ex: test.c | GCC,表示只有編譯器指定為 GCC 時才會編譯該檔案
- [Packages] - 標示引用到的其他 Packages 的 dec
- 每一行提供一個檔案,root的路徑為 EDKII 的根目錄
- 一般最常使用的 Uefi.h 檔案,由於其位於 MdePkg 目錄下,因此這邊就需要包含 MdePkg/MdePkg.dec
- [LibraryClasses] - 標示要鍊接到的 Module
- [BuildOptions] - 指定編譯和鍊接的選項參數 (可選非必須)
- 格式與 compiler 中常見的 istarget 非常類似
- [Compiler]: ==Target== _ ==ToolChain== _ ==ARCH== _ ==CFLAGS|LIBFLAGS== _ ==FLAGS==
- Compiler 可以是 GCC或INTEL(ICC)
- Target 可以是 DEBUG、RELEASE或是 =='*'==
- ToolChain 可以是 VS2015,GCC或ICC
- Arch 表示使用的ISA, =='*'== 表示所有ISA皆適用
- CFLAGS 編譯選項 LIBFLAGS鍊接選項
### <font color=#008000>EFI</font>
UEFI 的執行程式,載入記憶體後被的行程實例稱為 Image,
### UEFI power on flow

- SEC (Security Phase)
- Create CAR (Cache As Ram)
- Real address mode to flat mode
- PEI (Pre-EFI Initialzation)
### UEFI Package
##### Emulator 介面

##### UEFI data type
UEFI 中可以區分為 Windows 中的 __int64 跟 Linux 中的 long long,均表示為 64位的integer type。
其中常用的 EFI_STATUS 就是該資料型態。
### 常用工具
[intel udk debugger tool](https://www.intel.com/content/www/us/en/developer/articles/tool/unified-extensible-firmware-interface.html)
這裡存放除錯工具與使用手冊
### Reference
- Unified Extensible Firmware Interface (UEFI) Specification, Release 2.10
- https://book.huihoo.com/pc-architecture/