1. 程式人生 > 其它 >Arm64-UEFI 啟動Linux核心-Image檔案-PE格式

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:
        /*
         * DO 
NOT 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

……