• UEFI与inf文件


    UEFI与inf文件

    背景

    学习高通UEFI中的LCD显示框架,看到有些博客对inf文件进行了介绍,因此整理了这方面的一些入门知识。

    参考:

    inf文件的构成

    Defines

    INF_VERSION:Inf版本号,用于编译时解释inf文件,通常设置为0x00010005,INF_VERSION版本号便于EDKII系统升级。

    BASE_NAME:用于设置编译输出的文件名称,根据文件功能设置,不能包含空格。它通常也是输出文件名字。

    FILE_GUID:文件的全局唯一标识符,格式:8-4-4-4-12,用于生成固件。例如: 4ea97c46-7491-4dfd-b442-747010f3ce5f

    可以通过 http://www.guidgen.com/获得GUID。

    VERSION_STRING:文件版本字符串,用于标注文件的版本号

    MODUL_TYPE:用于标识模块的类型、应用工程文件设置为:UEF工_APPLICATION

    定义模块的模块类型,可以是SEC/PEI_CORE/PEIM/DXE_CORE/DXE_SAL_DRIVER/DXE_SMM_DRIVER/UEFI_DRIVER/DXE_DRIVER/DXE_RUNTIME_DRIVER/UEFI_APPLICATION/BASE中的一个。对于标准应用程序工程模块来说,为UEFI_APPLICATION类。
    

    ENTRY POINT:模块的入口函数

    Sources

    列出文件内所有的源文件和资源文件

    Packages

    列出文件使用的包的描述文件,即.dec文件,如果source块内包含了源文件,必须将MdePkg/MdePkg.dec放在首行

    LibraryClasses

    列出文件内所使用的库文件,应用工程文件必须包含UefiApplicationEntryPoint,驱动文件必须包含UEFIDriverEntryPoint

    Protocols

    列出文件内使用的Protocol的GUID

    BuildOptions

    编译配置。

    • = 表示选项附加到默认选项后面。
    • == 表示仅使用所定义的选项,弃用默认选项。

    附录

    初识UEFI

    按惯例,首先让我们用HelloWorld跟UEFI打个招呼吧

    标准application

    /*main.c */
    #include <Uefi.h>
    EFI_STATUS
        UefiMain (
        IN EFI_HANDLE        ImageHandle,
        IN EFI_SYSTEM_TABLE  *SystemTable
    )
    {
        SystemTable -> ConOut-> OutputString(SystemTable -> ConOut, L"HelloWorld
    "); 
        return EFI_SUCCESS;
    }
    

    有以下几点需要注意:

    1、 头文件, 所有的UEFI程序都有#include <Uefi.h>

    2、 main函数, UEFI 基本Application的main函数是UefiMain

    3、 main函数的返回值类型 EFI_STATUS。 在UEFI中基本上所有的返回值类型都是EFI_STATUS。它本质上是UINTN。

    4、 main函数的参数。.efi 文件加载到内存后称为Image, ImageHandle 用来描述、访问、控制此Image。 第二个参数是SystemTable,它是我们的程序同UEFI内核打交道的桥梁,通过它我们可以使用UEFI提供的各种服务,如Boot Services和 Runtime Services。 SystemTable是UEFI内核中的一个全局结构体。

    5、 输出是通过EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL的OutputString服务完成的。 服务(函数)的第一个参数是This指针,指向Protocol本身。 OutputString()的第二个参数是Unicode字符串。

    对应的inf文件

    要想编译main.c,我们还需要.inf文件, 在main.c所在的目录下编辑main.inf文件

    ##
    
    [Defines]
      INF_VERSION                    = 0x00010005
      BASE_NAME                      = main                    #输出文件的名字为 main.efi
      FILE_GUID                        = 6987936E-ED34-ffdb-AE97-1FA5E4ED2117
      MODULE_TYPE                   = UEFI_APPLICATION #模块类型: UEFI_DRIVER, DXE_DRIVER, DXE_RUNTIME_DRIVER,UEFI_APPLICATION,BASE,等
      VERSION_STRING               = 1.0
      ENTRY_POINT                    = UefiMain               #入口函数
    
    #
    # The following information is for reference only and not required by the build tools.
    #
    #  VALID_ARCHITECTURES           = IA32 X64 IPF EBC
    #
    
    # 源文件
    [Sources]  
       main.c 
    
    # .dec里面定义 include的路径
    [Packages]
      MdePkg/MdePkg.dec
    
    #要链接的库
    [LibraryClasses]
      UefiApplicationEntryPoint
      UefiLib
    
    [Protocols] 
    [FeaturePcd]
    [Pcd.common]
    [Guids]
     
    #编译选项, = 表示选项附加到默认选项后面。 == 表示仅使用所定义的选项,弃用默认选项。
    [BuildOptions]
      #MSFT:*_*_*_CC_FLAGS ==  /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1ib2 /GL  /EHs-c- /GR- /GF /Gy /Zi /Gm /D EFI_SPECIFICATION_VERSION=0x0002000A /D TIANO_RELEASE_VERSION=0x00080006 /FAs /Oi-
      #MSFT:*_*_*_CC_FLAGS =   /wd4804 
      #MSFT:Debug_*_IA32_CC_FLAGS = 
      #MSFT:Debug_*_X64_CC_FLAGS = 
      #MSFT:Release_*_IA32_CC_FLAGS = 
      #MSFT:Release_*_IA32_CC_FLAGS = 
      #MSFT:Release_*_IA32_DLINK_FLAGS = 
      #GCC:Release_*_IA32_CC_FLAGS = 
    

    然后将 main.inf 添加到 Nt32Pkg.dsc 或UnixPkg.dsc 的[Components]部分,

    例如添加下面一行(example目录在EDK2下)example/main/main.inf

    然后就可以使用BaseTools下的build进行编译了。

    Windows下执行:

    edksetup.bat
    build -p Nt32Pkg	32Pkg.dsc -a IA32
    

    Linux 执行

    source ./edksetup.sh BaseTools
    build -p UnixPkg/UnixPkg.dsc -a IA32
    

    其他类型的inf文件

    (1) 可以看出标准的application处理命令行参数不方便,UEFI提供了帮我们处理命令行参数的入口函数ShellCEntryLib。 我们要实现INTN ShellAppMain(UINTN Argc, CHAR16** Argv) 作为(开发者视角的)入口函数。

    /*Main.c */
    #include <Uefi.h>
    INTN
        EFIAPI
        ShellAppMain (
        IN UINTN Argc,
        IN CHAR16 **Argv
    )
    {
        gST -> ConOut-> OutputString(gST -> ConOut, L"HelloWorld
    "); 
        return 0;
    }
    

    inf文件。 我们需要连接ShellCEntryLib 库。

    [Defines]
      INF_VERSION                    = 0x00010006
      BASE_NAME                      = Main
      FILE_GUID                        = 4ea97c46-7491-4dfd-b442-747010f3ce5f
      MODULE_TYPE                   = UEFI_APPLICATION
      VERSION_STRING               = 0.1
      ENTRY_POINT                    = ShellCEntryLib
    
    #
    #  VALID_ARCHITECTURES           = IA32 X64 IPF
    #
    
    [Sources]
      Main.c
    
    [Packages]
      MdePkg/MdePkg.dec
      ShellPkg/ShellPkg.dec
    
    [LibraryClasses]   
      ShellCEntryLib
      UefiLib
    
    [BuildOptions]
    

    (2)使用main函数的application。如果你想像C一样使用main函数,那么你需要用到LibC。 LibC 中提供了ShellAppMain函数,我们要提供int main(int Argc, char** Argv)供其调用。

    /*Main.c */
    #include <Uefi.h>
    int
    EFIAPI
    main (
      IN int Argc,
      IN char **Argv
      )
    {
     gST -> ConOut-> OutputString(gST -> ConOut, L"HelloWorld
    "); 
      return 0;
    }
    

    真正的入口函数是ShellCEntryLib, 调用过程为 ShellCEntryLib -> ShellAppMain -> main.

    inf 文件: 我们需要连接ShellCEntryLib 和LibC库。

    [Defines]
      INF_VERSION                    = 0x00010006
      BASE_NAME                      = Main
      FILE_GUID                        = 4ea97c46-7491-4dfd-b442-747010f3ce5f
      MODULE_TYPE                   = UEFI_APPLICATION
      VERSION_STRING               = 0.1
      ENTRY_POINT                    = ShellCEntryLib
    
    #
    #  VALID_ARCHITECTURES           = IA32 X64 IPF
    #
    
    [Sources]
      Main.c
    
    [Packages]
      MdePkg/MdePkg.dec
      ShellPkg/ShellPkg.dec
    
    [LibraryClasses]   
      LibC
      ShellCEntryLib
      UefiLib
    
    [BuildOptions]
      MSFT:*_*_IA32_CC_FLAGS  = /Oi-
    
    

    还要再说明一点,如果你的程序中用到了printf(...)等等标准C的库函数,那么一定要使用此种类型的application。 因为ShellCEntryLib 函数中会调用ShellAppMain(...), StdLib的ShellAppMain(...) 会对stdlib 进行初始化。 然后才可以调用stdlib的函数。 (当然,如果你已经清楚地了解了入口函数的处理流程,你也可以手工调用StdLib的ShellAppMain进行出事后).

    (3)Lib 模块的inf文件。开发大型工程的时候我们会用到lib。

    例如我们要开发视频解码程序,会用到zlib库

    [Defines]
      INF_VERSION                    = 0x00010005
      BASE_NAME                      = zlib
      FILE_GUID                        = 348aaa62-BFBD-4882-9ECE-C80BBbbbb736
      VERSION_STRING               = 1.0
      MODULE_TYPE                   = BASE    #Base 表示此模块编译为library
      LIBRARY_CLASS                 = zlib
    
    #
    # The following information is for reference only and not required by the build tools.
    #
    #  VALID_ARCHITECTURES           = IA32 X64 IPF EBC
    #
    
    [Sources]
      adler32.c
      crc32.c
      deflate.c
      infback.c
      inffast.c
      inflate.c
      inftrees.c
      trees.c
      zutil.c
      compress.c
      uncompr.c
      gzclose.c
      gzlib.c
      gzread.c
      gzwrite.c
    
    [Packages]
      MdePkg/MdePkg.dec
      MdeModulePkg/MdeModulePkg.dec
      StdLib/StdLib.dec
    
    [LibraryClasses]
      MemoryAllocationLib
      BaseLib
      UefiBootServicesTableLib
      BaseMemoryLib
      UefiLib
      UefiRuntimeServicesTableLib
    
    [Protocols]
     
    [FeaturePcd]
    
    [Pcd]
    
    [Guids]
     
    [BuildOptions]
       GCC:*_*_IA32_CC_FLAGS = -D__UEFI__ -DLARGEFILE64_SOURCE=1  -w
    

    然后将 zlib|zlib-1.2.6/zlib.inf # zlib-1.2.6 在EKD2的根目录下放到.dsc 文件 [LibraryClasses]中。

    需要链接zlib的时候,在.inf文件的[LibraryClasses]中添加 zlib即可。

    (4)driver模块的inf文件。例如DiskIo的inf(MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf)

    [Defines]
      INF_VERSION                    = 0x00010005
      BASE_NAME                      = DiskIoDxe
      FILE_GUID                        = 6B38F7B4-AD98-40e9-9093-ACA2B5A253C4
      MODULE_TYPE                   = UEFI_DRIVER
      VERSION_STRING               = 1.0
      ENTRY_POINT                    = InitializeDiskIo
    
    #
    # The following information is for reference only and not required by the build tools.
    #
    #  VALID_ARCHITECTURES           = IA32 X64 IPF EBC
    #
    #  DRIVER_BINDING                =  gDiskIoDriverBinding
    #  COMPONENT_NAME                =  gDiskIoComponentName
    #  COMPONENT_NAME2               =  gDiskIoComponentName2
    #
    
    [Sources]
      ComponentName.c
      DiskIo.h
      DiskIo.c
    
    
    [Packages]
      MdePkg/MdePkg.dec
    
    [LibraryClasses]
      UefiBootServicesTableLib
      MemoryAllocationLib
      BaseMemoryLib
      BaseLib
      UefiLib
      UefiDriverEntryPoint
      DebugLib
    
    
    [Protocols]
      gEfiDiskIoProtocolGuid                        ## BY_START
      gEfiBlockIoProtocolGuid                       ## TO_START
    

    现在我们已经扫除了编译UEFI应用的所有障碍。

    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    DBSCAN 聚类分析
    常见空间聚类算法优劣概述
    最佳实践 —— 单元测试
    C/C++ 混合编程
    映射网络路径
    时间服务器/时间同步配置
    取出根路径
    PS Studio调用.exe输出错误信息的解决办法
    远程重启IIS服务
    浏览打开窗口,打开运行窗口,文件夹
  • 原文地址:https://www.cnblogs.com/schips/p/inf_in_uefi_.html
Copyright © 2020-2023  润新知