文章目錄
- 1 必須文件
- 2 運行環境
- 3 構建應用 (Visual Studio)
- 4 引用 EDK2 頭文件
1 必須文件
EDK2: 可以只拉取倉庫本身, 不拉取其子倉庫(完整構建才需要)
qemu: qemu 以源碼發布, QEMU for Windows – Installers (64 bit) 這里有民間構建的安裝包
2 運行環境
創建一個 root 文件夾, 用本機的文件夾作為"磁盤"
下面用 qemu 虛擬機來運行, 啟動 qemu 虛擬機, 加載 UEFI 固件, 運行 UEFI Shell
qemu-system-x86_64.exe ^-net none ^-hda fat:rw:root ^-drive if=pflash,format=raw,unit=0,file="C:\Program Files\qemu\share\edk2-x86_64-code.fd",readonly=on
-
-net none
關閉網絡加快啟動 -
-hda fat:rw:root
創建一個 MBR 分區的磁盤, 以 root 文件夾為磁盤內內容, 文件系統為 FATqemu 還支持 VHDX 虛擬磁盤, 選項為
-drive if=ide,format=vhdx,file=<虛擬磁盤路徑.vhdx>
-
-drive if=pflash,format=raw,unit=0,file="C:\Program Files\qemu\share\edk2-x86_64-code.fd",readonly=on
加載 UEFI 固件
啟動后, 由于 root 目錄中路徑 \EFI\Boot\bootx64.efi
位置為空, 默認運行 UEFI 固件自帶的 UEFI Shell
3 構建應用 (Visual Studio)
EFI 應用程序本質是一個 PE 程序, 使用 CFF Explorer 可以看到其 Subsystem 為 EFI Application (10)
Visual Studio 中創建一個 efitest 項目, 做如下配置
- 項目屬性頁>配置屬性>C/C++>常規>支持僅我的代碼調試>否
- 項目屬性頁>配置屬性>C/C++>常規>SDL 檢查>否
- 項目屬性頁>配置屬性>C/C++>預處理器>預處理器定義 在前面添加
_HAS_EXCEPTIONS=0;
- 項目屬性頁>配置屬性>C/C++>代碼生成>啟用C++異常>否
- (必須) 項目屬性頁>配置屬性>C/C++>代碼生成>基本運行時檢查>默認值
- 項目屬性頁>配置屬性>C/C++>代碼生成>安全檢查>禁用安全檢查
- 項目屬性頁>配置屬性>C/C++>命令行 添加
/Gs65536
- (可選) 項目屬性頁>配置屬性>鏈接器>輸入>附加依賴項 清空
- (可選) 項目屬性頁>配置屬性>鏈接器>輸入>忽略所有默認庫>是
- (必須) 項目屬性頁>配置屬性>鏈接器>清單文件>啟用用戶賬戶控制(UAC)>否
- (必須) 項目屬性頁>配置屬性>鏈接器>系統>子系統>EFI 應用程序
- (建議) 項目屬性頁>配置屬性>鏈接器>高級>入口點 填寫 UefiMain
- (必須) 項目屬性頁>配置屬性>鏈接器>高級>隨機基址>否
- (必須) 項目屬性頁>配置屬性>鏈接器>高級>數據執行保護>否
為方便進行部署, 在 項目屬性頁>配置屬性>生成事件>生成后事件 添加復制構建目標的命令
copy $(TargetPath) C:\Users\...\root\efitest.efi
編譯以下代碼
#include <stdint.h>
uint64_t UefiMain() {return 0xC0FFEE;
}
EFI Shell 中, 輸入命令
FS0:
efitest.efi
set
第 1 行通過 PATH 變量找到程序來運行, 第 2 行查看 lasterror 變量值, 應當為 0xC0FFEE
4 引用 EDK2 頭文件
EDK2 即 UEFI SDK, 其中 Uefi.h 包含了基本的數據結構可用
構建 EDK2 不夠簡易, 下面只依賴其頭文件中的數據結構
項目屬性頁>VC++目錄>包含目錄 添加 edk2\MdePkg\Include;edk2\MdePkg\Include\X64
C++ 中包含 EDK2 頭文件時需要用 extern “C” 包裹
下面是 Hello World 示例
extern "C" {
#include <Uefi.h>
}EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable
) {EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* Console = SystemTable->ConOut;Console->OutputString(Console, (CHAR16*)L"Hello world!\r\n");return EFI_SUCCESS;
}