Linux教程

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

……

 

这篇关于Arm64-UEFI 启动Linux内核-Image文件-PE格式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!