11.17-efi file

本文详细介绍了UEFI环境下基于EFI_FILE_PROTOCOL创建文件和目录的过程,以及如何将字符串写入和读取文件。讲解了从编写INF文件到编译生成EFI应用程序的步骤,包括模块的入口函数调用流程。同时,讨论了.dsc、.fdf、.dec文件的作用和内容。此外,还提及了文件系统协议在UEFI应用中的重要性以及遇到问题时的解决方法。
摘要由CSDN通过智能技术生成

11.17

任务目标 // 进度:

  • 基于 EFI_FILE_PROTOCOL 实现依次创建 ROOT->EFI->MYEFI->efi_template.txt 文件及目录,并操作文件将“DEBUG MAN”字符串写入文件,写入完成后,读取文件内容并输出至屏幕。

工作结果:

学习笔记:

\123\edk2-done cmd+Enter

\123\edk2-done>edksetup.bat

\123\edk2-done>build -p ShellPkg\ShellPkg.dsc -m ShellPkg\Application\ShellCTestApp\ShellCTestApp**.inf**


.inf

(Information) 模块的工程文件,一个.inf代表一个模块。整个项目以.inf文件为最小的单位组成。

文件组成

[Defines]块:

属性定义语法:属性名 = 属性值

[Defines] 
  INF_VERSION                    = 0x00010006    //必须 ,INF标准的版本号。edsk2的build会检查这个值而去解释.inf文件,最新的版本号是0x00010016,通常设置成0x00010015就行
  BASE_NAME                      = DxeCore    //必须,模块名字字符串,不能包含空格。它通常也是输出文件的名字,生成文件DxeMain.efi
  MODULE_UNI_FILE                = DxeCore.uni    //非必须 ,字符串资源文件
  FILE_GUID                      = dc72d2c7-a48a-42fd-80b6-9d229d9943c8    //必须, 8-4-4-4-4-12格式的唯一GUID,用于生成固件,
  MODULE_TYPE                    = UEFI_APPLICATION    //必须 ,定义模块的模块类型。SEC、PEIM、DXE_DEIVER等
  VERSION_STRING                 = 1.0    //必须, 模块的版本号字符串
  ENTRY_POINT                    = ShellCEntryLib    //必须,模块的入口函数

[Sources]块:

此块下的每一行表示一个文件,文件使用相对路径

[Sources]    //一般情况下不指定特定编译项时,使用这个即可,适用于任何体系结构
  Test.c

[Sources.IA32]    //编译32位模块时(build命令选项中指定了-a IA32选项),包含这个和Sources中的Test.c
  Cpu32.c

[Sources.X64]    //编译64位模块时,包含这个和Sources中的Test.c
  Cpu64.c
  
[Sources]
TimerWin.c | MSFT    //指定编译器有效
TimerLinux.c | GCC    //指定编译器有效

[Packages]块:
此块下面列出了模块所引用到的所有包的声明文件dec文件。如果在Sources块内列出了源文件,在此块下必须列出MdePkg/MePkg.dec,并放在此块的首行

[LibraryClasses]块:
此块内列出了模块所要链接的所有库模块(使用了库的库函数就要列出来)。库定义在包的dsc文件中

文件编译运行

源文件,inf工程文件完成,想要编译运行这个模块,需要将inf工程文件添加到dsc的[Components]部分,然后就可以使用build工具编译
模块应用程序是如何被编译成.efi文件的(这些过程都由build命令自动完成):
1、.c源码文件先被编译成目标文件.obj
2、连接器将目标文件*.obj和其他的库连接成*.dll
3、GenFw工具将*.dll转化成*.efi

标准应用程序加载过程(efi文件加载)

1、将efi文件加载到内存
efi模块文件————gBS ->LoadImage()将efi文件加载到内存中生成image对象————gBS->StartImage(Image)启动Image对象

gBS 指向启动服务表 global Boot Servers
gST 指向系统表 global System Table
gRT 指向运行时服务表 global Running Time
gImageHandle 指向正在执行的驱动或应用程序

EFI_STATUS
LoadDriver(
  IN CONST CHAR16   *FileName,
  IN CONST BOOLEAN  Connect
  )
{
  EFI_HANDLE                    LoadedDriverHandle;
  EFI_STATUS                    Status;
  EFI_DEVICE_PATH_PROTOCOL      *FilePath;
  EFI_LOADED_IMAGE_PROTOCOL     *LoadedDriverImage;

  LoadedDriverImage   = NULL;
  FilePath            = NULL;
  LoadedDriverHandle  = NULL;
  Status              = EFI_SUCCESS;

*************************************

  //
  // Use LoadImage to get it into memory
  //
  Status = gBS->LoadImage(
    FALSE,
    gImageHandle,
    FilePath,
    NULL,
    0,
    &LoadedDriverHandle);

**********************************************

    //
    // Make sure it is a driver image
    //
    Status = gBS->HandleProtocol (LoadedDriverHandle, &gEfiLoadedImageProtocolGuid, (VOID *) &LoadedDriverImage);

 *************************************************
 /*
 * 找出可执行的程序镜像Image的入口函数并执行找到的入口函数, gBS->StartImage是个函数指针,实际指向CoreStartImage函数
 */
    Status = gBS->StartImage(LoadedDriverHandle, NULL, NULL);
    if (EFI_ERROR(Status)) {
      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LOAD_ERROR), gShellLevel2HiiHandle, FileName, Status);
    } else {
      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LOAD_LOADED), gShellLevel2HiiHandle, FileName, LoadedDriverImage->ImageBase, Status);
    }
  }
**********************************************************
  return (Status);
}

2、进入镜像image的入口函数
CoreStartImage的主要作用是调用镜像的入口函数,在这里面SetIump/LongJumps是为应用程序提供了一种错误处理机制。gBS->StartImage的核心是EntryPoint函数,它就是这个应用程序的入口函数,就是_ModuleEntryPoint函数,进入到这里,代码执行的控制权才转交给我们的应用程序efi

EFI_STATUS
EFIAPI
CoreStartImage (
  IN EFI_HANDLE  ImageHandle,
  OUT UINTN      *ExitDataSize,
  OUT CHAR16     **ExitData  OPTIONAL
  )
{
  EFI_STATUS                    Status;
  LOADED_IMAGE_PRIVATE_DATA     *Image;
  LOADED_IMAGE_PRIVATE_DATA     *LastImage;
  UINT64                        HandleDatabaseKey;
  UINTN                         SetJumpFlag;
  EFI_HANDLE                    Handle;

**************************************************************
  //
  // Set long jump for Exit() support
  // JumpContext must be aligned on a CPU specific boundary.
  // Overallocate the buffer and force the required alignment
  //
  Image->JumpBuffer = AllocatePool (sizeof (BASE_LIBRARY_JUMP_BUFFER) + BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT);
  if (Image->JumpBuffer == NULL) {
    //
    // Image may be unloaded after return with failure,
    // then ImageHandle may be invalid, so use NULL handle to record perf log.
    //
    PERF_START_IMAGE_END (NULL);

    //
    // Pop the current start image context
    //
    mCurrentImage = LastImage;

    return EFI_OUT_OF_RESOURCES;
  }
  Image->JumpContext = ALIGN_POINTER (Image->JumpBuffer, BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT);

  SetJumpFlag = SetJump (Image->JumpContext);
  //
  // The initial call to SetJump() must always return 0.
  // Subsequent calls to LongJump() cause a non-zero value to be returned by SetJump().
  //
  if (SetJumpFlag == 0) {
    RegisterMemoryProfileImage (Image, (Image->ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION ? EFI_FV_FILETYPE_APPLICATION : EFI_FV_FILETYPE_DRIVER));
    //
    // Call the image's entry point 核心
    //
    Image->Started = TRUE;
    Image->Status = Image->EntryPoint (ImageHandle, Image->Info.SystemTable);

******************************************************************

  //
  // UEFI Specification - StartImage() - EFI 1.10 Extension
  // To maintain compatibility with UEFI drivers that are written to the EFI
  // 1.02 Specification, StartImage() must monitor the handle database before
  // and after each image is started. If any handles are created or modified
  // when an image is started, then EFI_BOOT_SERVICES.ConnectController() must
  // be called with the Recursive parameter set to TRUE for each of the newly
  // created or modified handles before StartImage() returns.
  //

**********************************************************************

  //
  // Save the Status because Image will get destroyed if it is unloaded.
  //
  Status = Image->Status;

**************************************************************************
  //
  // Done
  //
  PERF_START_IMAGE_END (Handle);
  return Status;
}

_ModuleEntryPoint函数:

EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_PEI_FILE_HANDLE       FileHandle,
  IN CONST EFI_PEI_SERVICES    **PeiServices
  )
{
  if (_gPeimRevision != 0) {
    //
    // Make sure that the PEI spec revision of the platform is >= PEI spec revision of the driver
    //
    ASSERT ((*PeiServices)->Hdr.Revision >= _gPeimRevision);
  }

  //
  // Call constructor for all libraries
  //
  ProcessLibraryConstructorList (FileHandle, PeiServices);

  //
  // Call the driver entry point
  //
  return ProcessModuleEntryPointList (FileHandle, PeiServices);
}

3、进入模块的入口函数
在_ModuleEntryPoint中,调用了ProcessModuleEntryPointList ,然后在build过程中,会解析inf文件,生成AutoGen.c文件,里面查看ProcessModuleEntryPointList ,它调用了应用程序工程模块的真正入口函数,就是inf文件中指定的那个入口函数

EFI_STATUS
EFIAPI
ProcessModuleEntryPointList (
  IN       EFI_PEI_FILE_HANDLE  FileHandle,
  IN CONST EFI_PEI_SERVICES     **PeiServices
  )

{
  return PcdPeimInit (FileHandle, PeiServices);
}

标准应用程序工程模块入口函数调用过程:
efi——LoadImage——StartImage——_ModuleEntryPoint——ProcessModuleEntryPointList ——inf中指定的入口函数

.dsc

(Platform Description File) 用于编译一整个Package,包含了[Define]、[LibraryClasses]、[Components]重要部分。

[LibraryClasses]块中定义了库的名字以及库.inf文件的路径,这些库可以被[Components]块内的模块引用。

[Components]块,在该区块内定义的模块都会被build工具编译并生成.efi文件。

.fdf

(Flash Description File)用于生成固件Image

.dec

(Package Declaration)用于声明本Package当中包含的内容,可以看做一个大型的.h头文件。

在这个的Package项目包下,除了.dsc,.fdf,.dec等,还可以建立些Drivers目录(存放驱动类型的模块),Library目录(存放库类型的模块),Include(存放被调用的头文件)等等。


读取文件会需要一个协议 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL


如果出现了找不到library class,可以查看下是否 [LibraryClasses]有对应的包,或者有包的话是否打开。


UEFI标准应用程序工程模块文件 -inf 随记

uefi读取文件

UEFI开发与调试—文件操作

uefi读硬盘和分区数据

uefi 下面如何创建文件夹

编写uefi的lib时的一个错误

UEFI EDK2 编译报找不到library class如何办?

心情感悟:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值