Arm64-UEFI 啟動Linux核心-Image檔案-PE格式
背景介紹:
參考:
http://www.wowotech.net/linux_kenrel/UEFI.html
1、UEFI是什麼鬼?
BIOS實際上就是IBM PC相容機(多麼古老的一個詞彙啊)主機板上的韌體(firmware),這些韌體可以在系統啟動過程中初始化硬體,self test,載入bootloader或者OS kernel,並且能為OS提供一些基礎的服務。
Intel提出來EFI(Extensible Firmware Interface)來取代BIOS interface。2005年,Intel終止了EFI規範的開發,替代它的是Unified EFI Forum負責的UEFI
2、UEFI關ARM什麼事?
如果ARM僅僅是將目光放在移動(嵌入式)市場,那麼UEFI當然不關ARM什麼事情。
但是,在推出ARMv8以及64 bit架構的的處理器之後,ARM的野心已經不滿足在移動市場上稱王了。
3、UEFI如何定義系統的啟動過程?
在UEFI規範中定義了BOOT manager,它會根據儲存在NVRAM中的引數來決定如何載入 EFI Application
EFI Application 是 PE(Portable Executable )格式的檔案。
PE格式 是一種二進位制可執行檔案的格式(在linux世界中,我們多半熟悉的是ELF格式),由微軟開發,廣泛應用在Windows平臺上。
uefi 的boot manager --> PE 格式的 bootloader (Uboot) --> linux kernel ? 這樣不夠直觀。PE 格式的 uboot 只是轉接資訊。
因此,linux kernel image自身也可以包裝成一個EFI application image,由boot manager直接載入,完成啟動過程。
uefi 的boot manager --> PE 格式的 linux kernel .
PE格式-Linux核心Image
PE 格式
下面的圖片是一個PE檔案格式的示意圖:
arm64 kernel image 生成PE格式
MS DOS頭裡面的內容,由 arch/arm64/kernel/head.S 填充。
head.S 內容
__HEAD _head: /* * DONOT MODIFY. Image header expected by Linux boot-loaders. */ #ifdef CONFIG_EFI /* * This add instruction has no meaningful effect except that * its opcode forms the magic "MZ" signature required by UEFI. */ add x13, x18, #0x16 b primary_entry #else b primary_entry // branch to kernel start, magic .long 0 // reserved #endif .quad 0 // Image load offset from start of RAM, little-endian le64sym _kernel_size_le // Effective size of kernel image, little-endian le64sym _kernel_flags_le // Informative flags, little-endian .quad 0 // reserved .quad 0 // reserved .quad 0 // reserved .ascii ARM64_IMAGE_MAGIC // Magic number #ifdef CONFIG_EFI .long pe_header - _head // Offset to the PE header. pe_header: __EFI_PE_HEADER #else .long 0 // reserved #endif
其中ARM64_IMAGE_MAGIC是一個魔數,4 個位元組, 其定義如下(程式碼位於arch/arm64/include/asm/image.h
#define ARM64_IMAGE_MAGIC "ARM\x64"
上面,從 _head 到 pe_header 之間有 7*8 bytes + 4 bytes + 4bytes
MS DOS 結構體
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // MZ標記 0x5a4d 2 byte WORD e_cblp; // 最後(部分)頁中的位元組數 2 byte WORD e_cp; // 檔案中的全部和部分頁數 2 byte WORD e_crlc; // 重定位表中的指標數 2 byte WORD e_cparhdr; // 頭部尺寸以段落為單位 2 byte WORD e_minalloc; // 所需的最小附加段 2 byte WORD e_maxalloc; // 所需的最大附加段 2 byte WORD e_ss; // 初始的SS值(相對偏移量) 2 byte WORD e_sp; // 初始的SP值 2 byte WORD e_csum; // 補碼校驗值 2 byte WORD e_ip; // 初始的IP值 2 byte WORD e_cs; // 初始的SS值 2 byte WORD e_lfarlc; // 重定位表的位元組偏移量 2 byte WORD e_ovno; // 覆蓋號 2 byte WORD e_res[4]; // 保留字 8 bytes WORD e_oemid; // OEM識別符號(相對m_oeminfo) 2 byte WORD e_oeminfo; // OEM資訊 2 byte WORD e_res2[10]; // 保留字 10 * 2 bytes LONG e_lfanew; // NT頭(PE標記)相對於檔案的偏移地址 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
這一結構體有 4 + 4 + 2*8 + 2* 8 + 3*8 = 7*8 + 4 + 4 個位元組
head.S 的7*8 + 4 + 4 個位元組 可以和 結構體的7*8 + 4 + 4 個位元組 一一匹配。 主要就是 e_lfanew 對應到 head.S 裡面的
.long pe_header - _head
pe header 和arch/arm64/kernel/efi-header.S 內容
head.S 中 pe_header 後面跟的__EFI_PE_HEADER 是一個巨集定義,在arch/arm64/kernel/efi-header.S 中,裡面的內容和 PE 檔案格式一一對應
.macro __EFI_PE_HEADER .long PE_MAGIC coff_header: .short IMAGE_FILE_MACHINE_ARM64 // Machine .short section_count // NumberOfSections .long 0 // TimeDateStamp .long 0 // PointerToSymbolTable .long 0 // NumberOfSymbols .short section_table - optional_header // SizeOfOptionalHeader .short IMAGE_FILE_DEBUG_STRIPPED | \ IMAGE_FILE_EXECUTABLE_IMAGE | \ IMAGE_FILE_LINE_NUMS_STRIPPED // Characteristics optional_header: .short PE_OPT_MAGIC_PE32PLUS // PE32+ format .byte 0x02 // MajorLinkerVersion .byte 0x14 // MinorLinkerVersion .long __initdata_begin - efi_header_end // SizeOfCode .long __pecoff_data_size // SizeOfInitializedData .long 0 // SizeOfUninitializedData .long __efistub_efi_pe_entry - _head // AddressOfEntryPoint .long efi_header_end - _head // BaseOfCode extra_header_fields: .quad 0 // ImageBase .long SEGMENT_ALIGN // SectionAlignment
……