EDK2文件系统—新创建一个Fv卷标FS03

实现卷标FS03和Fv文件显示


1.显示

在开机进入shell后,我们可以看见卷标FS01和FS02,通过执行FS01:可进入Fv1 root目录。

再通过ls可显示添加在Fv1中的各个文件。我们可以对各个文件进行操作。

代码实际上是实现在ShellPkg中的ls/efi.c。

但是当我们新增加一个Fv时,理应会在shell中显式卷标FS03,但是发现没有显式出来,我们无法以文件系统的方式访问Fv3上的文件。这是为什么呢?

2.依赖的协议

要实现按照文件的方式访问Fv, 我们需要实现EFI_SIMPLE_FILE_SYSTEM_PROTOCOL协议。

例如map命令的实现。遍历所有的卷标,就是遍历所有安装了gEfiSimpleFileSystemProtocolGuid协议的Fv。

//ShellPkg/Library/UefiShellLevel2CommandsLib/Map.c
507  SHELL_STATUS
508  PerformMappingDisplay(
509    IN CONST BOOLEAN Verbose,
510    IN CONST BOOLEAN Consist,
511    IN CONST BOOLEAN Normal,
512    IN CONST CHAR16  *TypeString,
513    IN CONST BOOLEAN SFO,
514    IN CONST CHAR16  *Specific OPTIONAL,
515    IN CONST BOOLEAN Header
516    )
562    //
563    // Look up all SimpleFileSystems in the platform
564    //
565    Status = gBS->LocateHandle(
566      ByProtocol,
567      &gEfiSimpleFileSystemProtocolGuid,
568      NULL,
569      &BufferSize,
570      HandleBuffer);
571    if (Status == EFI_BUFFER_TOO_SMALL) {
572      HandleBuffer = AllocateZeroPool(BufferSize);
573      if (HandleBuffer == NULL) {
574        return (SHELL_OUT_OF_RESOURCES);
575      }
576      Status = gBS->LocateHandle(
577        ByProtocol,
578        &gEfiSimpleFileSystemProtocolGuid,
579        NULL,
580        &BufferSize,
581        HandleBuffer);
582    }
....

除此之外还依赖BlockIo协议。

总结:

如果想要某个Fv能按照文件的方式操作,或以目录的方式显示,需要给Fv 的Handle安装gEfiSimpleFileSystemProtocolGuid协议。

3. 如何给Fv安装文件系统协议

通过实现MdeModulePkg/Universal/FvSimpleFileSystemDxe模块。

可以看到该模块就实现了gEfiSimpleFileSystemProtocolGuid协议。

//edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemEntryPoint.c#464
414  EFI_STATUS
415  EFIAPI
416  FvSimpleFileSystemDriverStart (
417    IN  EFI_DRIVER_BINDING_PROTOCOL  *DriverBinding,
418    IN  EFI_HANDLE                   ControllerHandle,
419    IN  EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
420    )
421  {
    ...
462    Status = gBS->InstallProtocolInterface(
463                    &ControllerHandle,
464                    &gEfiSimpleFileSystemProtocolGuid,
465                    EFI_NATIVE_INTERFACE,
466                    &Instance->SimpleFs
467                    );
468    ASSERT_EFI_ERROR (Status);

可以看到,上述函数是UEFI驱动模块的Start函数。

什么样的 Handle才能支持上述Start函数的运行呢,看Support函数。

382  EFI_STATUS
383  EFIAPI
384  FvSimpleFileSystemDriverSupported (
385    IN  EFI_DRIVER_BINDING_PROTOCOL  *DriverBinding,
386    IN  EFI_HANDLE                   ControllerHandle,
387    IN  EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
388    )
389  {
390    return gBS->OpenProtocol (
391                  ControllerHandle,
392                  &gEfiFirmwareVolume2ProtocolGuid,
393                  NULL,
394                  gImageHandle,
395                  ControllerHandle,
396                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
397                  );
398  }

也就是说,只要这个模块实现了gEfiFirmwareVolume2ProtocolGuid 协议,再添加上FvSimpleFileSystemDxe模块,就能支持EfiSimpleFileSystemProtocolGuid 文件系统协议。

我们继续看下这个文件中 FvSimpleFileSystemDriverStart 函数的实现:

414  EFI_STATUS
415  EFIAPI
416  FvSimpleFileSystemDriverStart (
417    IN  EFI_DRIVER_BINDING_PROTOCOL  *DriverBinding,
418    IN  EFI_HANDLE                   ControllerHandle,
419    IN  EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
420    )
421  {
    ...
434    //
435    // Open FV protocol
436    //
437    Status = gBS->OpenProtocol (
438                    ControllerHandle,
439                    &gEfiFirmwareVolume2ProtocolGuid,
440                    (VOID **) &FvProtocol,
441                    gImageHandle,
442                    ControllerHandle,
443                    EFI_OPEN_PROTOCOL_BY_DRIVER
444                    );
445    if (EFI_ERROR (Status)) {
446      return Status;
447    }
448  
462    Status = gBS->InstallProtocolInterface(
463                    &ControllerHandle,
464                    &gEfiSimpleFileSystemProtocolGuid,
465                    EFI_NATIVE_INTERFACE,
466                    &Instance->SimpleFs
467                    );
468    ASSERT_EFI_ERROR (Status);

所以,支持Fv文件系统协议的条件是:Fv 安装了固件卷协议gEfiFirmwareVolume2ProtocolGuid。

4.给Fv产生固件卷协议gEfiFirmwareVolume2ProtocolGuid

如何实现固件卷协议呢? Dxe Core 的FwVolBlock/ Services已经帮我们实现了。

我们主要看下DxeMain的固件卷服务中的实现。

  1. ProcessFirmwareVolume服务安装gEfiFirmwareVolume2ProtocolGuid协议。

    这是启动服务表中的一个服务ProcessFirmwareVolume 。

    //MdeModulePkg/Core/Dxe/FwVolBlock/FwVolBlock.c
    681  CoreProcessFirmwareVolume (
    682    IN VOID                             *FvHeader,
    683    IN UINTN                            Size,
    684    OUT EFI_HANDLE                      *FVProtocolHandle
    685    )
    686  {
    687    VOID        *Ptr;
    688    EFI_STATUS  Status;
    689  
    690    *FVProtocolHandle = NULL;
    691    Status = ProduceFVBProtocolOnBuffer (
    692              (EFI_PHYSICAL_ADDRESS) (UINTN) FvHeader,
    693              (UINT64)Size,
    694              NULL,
    695              0,
    696              FVProtocolHandle
    697              );
    698    //
    699    // Since in our implementation we use register-protocol-notify to put a
    700    // FV protocol on the FVB protocol handle, we can't directly verify that
    701    // the FV protocol was produced. Therefore here we will check the handle
    702    // and make sure an FV protocol is on it. This indicates that all went
    703    // well. Otherwise we have to assume that the volume was corrupted
    704    // somehow.
    705    //
    706    if (!EFI_ERROR(Status)) {
    707      ASSERT (*FVProtocolHandle != NULL);
    708      Ptr = NULL;
    709      Status = CoreHandleProtocol (*FVProtocolHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **) &Ptr);
    710      if (EFI_ERROR(Status) || (Ptr == NULL)) {
    711        return EFI_VOLUME_CORRUPTED;
    712      }
    713      return EFI_SUCCESS;
    714    }
    715    return Status;
    716  }
    717  
    

    这个函数调用CoreHandleProtocol 产生了协议gEfiFirmwareVolume2ProtocolGuid。

    同时,还调用了一个函数ProduceFVBProtocolOnBuffer 。

    同时还调用了ProduceFVBProtocolOnBuffer 函数。

  2. ProduceFVBProtocolOnBuffer 函数产生gEfiFirmwareVolumeBlockProtocolGuid协议。

    //MdeModulePkg/Core/Dxe/FwVolBlock/FwVolBlock.c#709
    445  EFI_STATUS
    446  ProduceFVBProtocolOnBuffer (
    447    IN EFI_PHYSICAL_ADDRESS   BaseAddress,
    448    IN UINT64                 Length,
    449    IN EFI_HANDLE             ParentHandle,
    450    IN UINT32                 AuthenticationStatus,
    451    OUT EFI_HANDLE            *FvProtocol  OPTIONAL
    452    )
    453  {
    585    //
    586    // Attach FvVolBlock Protocol to new handle
    587    //
    588    Status = CoreInstallMultipleProtocolInterfaces (
    589               &FvbDev->Handle,
    590               &gEfiFirmwareVolumeBlockProtocolGuid,     &FvbDev->FwVolBlockInstance,
    591               &gEfiDevicePathProtocolGuid,              FvbDev->DevicePath,
    592               NULL
    593               );
    594  
    595    //
    596    // If they want the handle back, set it.
    597    //
    598    if (FvProtocol != NULL) {
    599      *FvProtocol = FvbDev->Handle;
    600    }
    601  
    602    return Status;
    603  }
    
    

可见,可以调用gBS->ProcessFirmwareVolume 服务来安装固件卷协议1(gEfiFirmwareVolumeBlockProtocolGuid)和固件卷协议2.(gEfiFirmwareVolume2BlockProtocolGuid).

其实上述分析不准确。gEfiFirmwareVolume2BlockProtocolGuid的安装是通过回调函数NotifyFwVolBlock。只要安装了gEfiFirmwareVolumeBlockProtocolGuid协议就会触发一个事件,回调NotifyFwVolBlock函数安装gEfiFirmwareVolume2BlockProtocolGuid。

//MdeModulePkg/Core/Dxe/FwVol/FwVol.c
2  EFI_STATUS
713  EFIAPI
714  FwVolDriverInit (
715    IN EFI_HANDLE                   ImageHandle,
716    IN EFI_SYSTEM_TABLE             *SystemTable
717    )
718  {
719    gEfiFwVolBlockEvent = EfiCreateProtocolNotifyEvent (
720                            &gEfiFirmwareVolumeBlockProtocolGuid,
721                            TPL_CALLBACK,
722                            NotifyFwVolBlock,
723                            NULL,
724                            &gEfiFwVolBlockNotifyReg
725                            );
726    return EFI_SUCCESS;
727  }
728  

那我们看下哪里安装了gEfiFirmwareVolumeBlockProtocolGuid协议。

5. 什么条件下触发gBS->ProcessFirmwareVolume安装固件卷协议

主要还是DxeCore入口函数DxeMain中进行的,我们先捋一捋。

DxeMain :

//MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c
231  VOID
232  EFIAPI
233  DxeMain (
234    IN  VOID *HobStart
235    )
236  {
       ....
485    //
486    // Produce Firmware Volume Protocols, one for each FV in the HOB list.
487    //
488    Status = FwVolBlockDriverInit (gDxeCoreImageHandle, gDxeCoreST);
489    ASSERT_EFI_ERROR (Status);
490  
491    Status = FwVolDriverInit (gDxeCoreImageHandle, gDxeCoreST);
492    ASSERT_EFI_ERROR (Status);
493  
  1. 先从HOB List遍历所有添加再HOB的FV。

    获取Fv的BaseAddress 、Length 、Length 等。

    617  EFI_STATUS
    618  EFIAPI
    619  FwVolBlockDriverInit (
    620    IN EFI_HANDLE                 ImageHandle,
    621    IN EFI_SYSTEM_TABLE           *SystemTable
    622    )
    623  {
          ....
    628    //
    629    // Core Needs Firmware Volumes to function
    630    //
    631    FvHob.Raw = GetHobList ();
    632    while ((FvHob.Raw = GetNextHob (EFI_HOB_TYPE_FV, FvHob.Raw)) != NULL) {
    633      AuthenticationStatus = 0;
    634      //
    635      // Get the authentication status propagated from PEI-phase to DXE.
    636      //
    637      Fv3Hob.Raw = GetHobList ();
    638      while ((Fv3Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV3, Fv3Hob.Raw)) != NULL) {
    639        if ((Fv3Hob.FirmwareVolume3->BaseAddress == FvHob.FirmwareVolume->BaseAddress) &&
    640            (Fv3Hob.FirmwareVolume3->Length == FvHob.FirmwareVolume->Length)) {
    641          AuthenticationStatus = Fv3Hob.FirmwareVolume3->AuthenticationStatus;
    642          break;
    643        }
    644        Fv3Hob.Raw = GET_NEXT_HOB (Fv3Hob);
    645      }
    
    
  2. 为找到的Fv产生固件卷协议

    618  EFIAPI
    619  FwVolBlockDriverInit (
    620    IN EFI_HANDLE                 ImageHandle,
    621    IN EFI_SYSTEM_TABLE           *SystemTable
    622    )
    623  {
               .....
    646      //
    647      // Produce an FVB protocol for it
    648      //
    649      ProduceFVBProtocolOnBuffer (FvHob.FirmwareVolume->BaseAddress, FvHob.FirmwareVolume->Length, NULL, AuthenticationStatus, NULL);
    650      FvHob.Raw = GET_NEXT_HOB (FvHob);
    651    }
    
    

    主要通过函数ProduceFVBProtocolOnBuffer 。该函数中安装了gEfiFirmwareVolumeBlockProtocolGuid协议,同时会触发gEfiFirmwareVolume2ProtocolGuid协议的安装。

    446  ProduceFVBProtocolOnBuffer (
    447    IN EFI_PHYSICAL_ADDRESS   BaseAddress,
    448    IN UINT64                 Length,
    449    IN EFI_HANDLE             ParentHandle,
    450    IN UINT32                 AuthenticationStatus,
    451    OUT EFI_HANDLE            *FvProtocol  OPTIONAL
    452    )
    583  
            ....
    584    //
    585    //
    586    // Attach FvVolBlock Protocol to new handle
    587    //
    588    Status = CoreInstallMultipleProtocolInterfaces (
    589               &FvbDev->Handle,
    590               &gEfiFirmwareVolumeBlockProtocolGuid,     &FvbDev->FwVolBlockInstance,
    591               &gEfiDevicePathProtocolGuid,              FvbDev->DevicePath,
    592               NULL
    593               );
    594  
    595    //
    
  3. FwVolDriverInit是个入口函数。注册了gEfiFirmwareVolume2ProtocolGuid协议的安装函数NotifyFwVolBlock。

    2  EFI_STATUS
    713  EFIAPI
    714  FwVolDriverInit (
    715    IN EFI_HANDLE                   ImageHandle,
    716    IN EFI_SYSTEM_TABLE             *SystemTable
    717    )
    718  {
    719    gEfiFwVolBlockEvent = EfiCreateProtocolNotifyEvent (
    720                            &gEfiFirmwareVolumeBlockProtocolGuid,
    721                            TPL_CALLBACK,
    722                            NotifyFwVolBlock,
    723                            NULL,
    724                            &gEfiFwVolBlockNotifyReg
    725                            );
    726    return EFI_SUCCESS;
    727  }
    
    

    NotifyFwVolBlock函数安装gEfiFirmwareVolume2ProtocolGuid协议

    VOID
    580  EFIAPI
    581  NotifyFwVolBlock (
    582    IN  EFI_EVENT Event,
    583    IN  VOID      *Context
    584    )
    585  {
           ...
    
    620      //
    621      // Get the FirmwareVolumeBlock protocol on that handle
    622      //
    623      Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
    624      ASSERT_EFI_ERROR (Status);
              ...
    641      //
    642      // Check if there is an FV protocol already installed in that handle
    643      //
    644      Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv);
             ...
    
    676        if (!EFI_ERROR (FvCheck (FvDevice))) {
    677          //
    678          // Install an New FV protocol on the existing handle
    679          //
    680          Status = CoreInstallProtocolInterface (
    681                      &Handle,
    682                      &gEfiFirmwareVolume2ProtocolGuid,
    683                      EFI_NATIVE_INTERFACE,
    684                      &FvDevice->Fv
    685                      );
                  .....
    

那么为什么FwVolDriverInit函数还在FwVolBlockDriverInit函数执行之后呢?如何能确保gEfiFirmwareVolumeBlockProtocolGuid的安装事件触发gEfiFirmwareVolume2ProtocolGuid,的安装。

因此此时事件机制还没有生效,已生效立马就会检查。检查时发现gEfiFirmwareVolumeBlockProtocolGuid已经安装,只要事件机制使能FwVolDriverInit函数就会被触发的。

6.RootCause

因此,我们再FDF中新创建的Fv,没有产生卷标的原因就是,我们的PEI阶段没有将它添加到Hob List。

7.将Fv 添加到Hob List的实现

1.初始化FV HOB

//ArmPlatformPkg/PrePi/PrePi.c
55  VOID
56  PrePiMain (
57    IN  UINTN                     UefiMemoryBase,
58    IN  UINTN                     StacksBase,
59    IN  UINT64                    StartTimeStamp
60    )
.....
140    // Initialize Platform HOBs (CpuHob and FvHob)
141    Status = PlatformPeim ();
142    ASSERT_EFI_ERROR (Status);

通过BuildFvHob 构造最初级的Fv Hob。也就是第一个Fv,即Fv1(压缩的Fv)。通过我们FDF中设置的参数,PcdFvBaseAddress和PcdFvSize。

//ArmPlatformPkg/PlatformPei/PlatformPeiLib.c#17
17  PlatformPeim (
18    VOID
19    )
20  {
21    BuildFvHob (PcdGet64 (PcdFvBaseAddress), PcdGet32 (PcdFvSize));
22  
23    return EFI_SUCCESS;
24  }
25  

FV Hob中当前只描述了第一个Fv,即压缩的Fv。它的地址就是FD的加载地址。

2.PrePiMain 解压F第一个Fv

//ArmPlatformPkg/PrePi/PrePi.c
55  VOID
56  PrePiMain (
57    IN  UINTN                     UefiMemoryBase,
58    IN  UINTN                     StacksBase,
59    IN  UINT64                    StartTimeStamp
60    )
    ....
150    // Assume the FV that contains the SEC (our code) also contains a compressed FV.
151    Status = DecompressFirstFv ();
152    ASSERT_EFI_ERROR (Status);
153  

该函数主要做了两件事情:

  • 根据文件类型EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, 从压缩Fv1中找到第一个非压缩的Fv文件,实际上是FvMain。
  • 通过FfsProcessFvFile对找到的Fv进行处理。包括提取Fv中内容,将该Fv2添加到Fv HOB List。
//EmbeddedPkg/Library/PrePiLib/PrePiLib.c
233  EFI_STATUS
234  EFIAPI
235  DecompressFirstFv (
236    VOID
237    )
238  {
239    EFI_STATUS          Status;
240    EFI_PEI_FV_HANDLE   VolumeHandle;
241    EFI_PEI_FILE_HANDLE FileHandle;
242  
243    Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, &VolumeHandle, &FileHandle);
244    if (!EFI_ERROR (Status)) {
245      Status = FfsProcessFvFile (FileHandle);
246    }

我们分别来看他们的实现。

3.FfsAnyFvFindFirstFile找到一个非压缩的Fv

函数参数:

  • 文件类型EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE
  • 找到的Fv的VolumeHandle
  • 找到的Fv的FileHandle
//EmbeddedPkg/Library/PrePiLib/PrePiLib.c?
744  FfsAnyFvFindFirstFile (
745    IN  EFI_FV_FILETYPE       FileType,
746    OUT EFI_PEI_FV_HANDLE     *VolumeHandle,
747    OUT EFI_PEI_FILE_HANDLE   *FileHandle
748    )
749  {
750    EFI_STATUS        Status;
751    UINTN             Instance;
       。。。
758  
759    while (1)
760    {
761      Status = FfsFindNextVolume (Instance++, VolumeHandle);
762      if (EFI_ERROR (Status))
763      {
764        break;
765      }
766  
767      Status = FfsFindNextFile (FileType, *VolumeHandle, FileHandle);
768      if (!EFI_ERROR (Status))
769      {
770        break;
771      }

775  }
776  

实际上 FfsFindNextVolume 函数实现很简单。就是从已经初始化的Fv Hob List中找出第一个Fv。卡面初始化时,第一个Fv就是压缩的Fv1,首地址就是FD加载地址。因此,返回的就是压缩Fv1的地址=FD 地址。

主要看FfsFindNextFile函数的实现,参数:

  • 文件类型EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE
  • 压缩的Fv 基地址
  • 要找到的第一个非压缩Fv 的地址
13  FfsFindNextFile (
514    IN UINT8                       SearchType,
515    IN EFI_PEI_FV_HANDLE           VolumeHandle,
516    IN OUT EFI_PEI_FILE_HANDLE     *FileHandle
517    )
518  {
519    return FindFileEx (VolumeHandle, NULL, SearchType, FileHandle);
520  }

继续查看FindFileEx函数。

56  EFI_STATUS
157  FindFileEx (
158    IN  CONST EFI_PEI_FV_HANDLE        FvHandle, // 压缩Fv的地址
159    IN  CONST EFI_GUID                 *FileName,   OPTIONAL
160    IN        EFI_FV_FILETYPE          SearchType, //文件类型
161    IN OUT    EFI_PEI_FILE_HANDLE      *FileHandle //要返回的非压缩Fv的地址
162    )
164    EFI_FIRMWARE_VOLUME_HEADER           *FwVolHeader;
165    EFI_FFS_FILE_HEADER                   **FileHeader;
166    EFI_FFS_FILE_HEADER                   *FfsFileHeader;
167    EFI_FIRMWARE_VOLUME_EXT_HEADER        *FwVolExHeaderInfo;
168    UINT32                                FileLength;
169    UINT32                                FileOccupiedSize;
170    UINT32                                FileOffset;
171    UINT64                                FvLength;
172    UINT8                                 ErasePolarity;
173    UINT8                                 FileState;
174  
175    FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FvHandle;
176    FileHeader  = (EFI_FFS_FILE_HEADER **)FileHandle;
177  
178    FvLength = FwVolHeader->FvLength;
179    if (FwVolHeader->Attributes & EFI_FVB2_ERASE_POLARITY) {
180      ErasePolarity = 1;
181    } else {
182      ErasePolarity = 0;
183    }
184  
185    //
186    // If FileHeader is not specified (NULL) or FileName is not NULL,
187    // start with the first file in the firmware volume.  Otherwise,
188    // start from the FileHeader.
189    //
190    if ((*FileHeader == NULL) || (FileName != NULL)) {
191      FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FwVolHeader + FwVolHeader->HeaderLength);
192      if (FwVolHeader->ExtHeaderOffset != 0) {
193        FwVolExHeaderInfo = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)(((UINT8 *)FwVolHeader) + FwVolHeader->ExtHeaderOffset);
194        FfsFileHeader = (EFI_FFS_FILE_HEADER *)(((UINT8 *)FwVolExHeaderInfo) + FwVolExHeaderInfo->ExtHeaderSize);
195      }
196    } else {
197      //
198      // Length is 24 bits wide so mask upper 8 bits
199      // FileLength is adjusted to FileOccupiedSize as it is 8 byte aligned.
200      //
201      FileLength = *(UINT32 *)(*FileHeader)->Size & 0x00FFFFFF;
202      FileOccupiedSize = GET_OCCUPIED_SIZE (FileLength, 8);
203      FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)*FileHeader + FileOccupiedSize);
204    }
205  
206    // FFS files begin with a header that is aligned on an 8-byte boundary
207    FfsFileHeader = ALIGN_POINTER (FfsFileHeader, 8);
208  
209    FileOffset = (UINT32) ((UINT8 *)FfsFileHeader - (UINT8 *)FwVolHeader);
210    ASSERT (FileOffset <= 0xFFFFFFFF);
211  
212    while (FileOffset < (FvLength - sizeof (EFI_FFS_FILE_HEADER))) {
213      //
214      // Get FileState which is the highest bit of the State
215      //
216      FileState = GetFileState (ErasePolarity, FfsFileHeader);
217  
218      switch (FileState) {
219  
220      case EFI_FILE_HEADER_INVALID:
221        FileOffset += sizeof(EFI_FFS_FILE_HEADER);
222        FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + sizeof(EFI_FFS_FILE_HEADER));
223        break;
224  
225      case EFI_FILE_DATA_VALID:
226      case EFI_FILE_MARKED_FOR_UPDATE:
227        if (CalculateHeaderChecksum (FfsFileHeader) != 0) {
228          ASSERT (FALSE);
229          *FileHeader = NULL;
230          return EFI_NOT_FOUND;
231        }
232  
233        FileLength = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF;
234        FileOccupiedSize = GET_OCCUPIED_SIZE(FileLength, 8);
235  
236        if (FileName != NULL) {
237          if (CompareGuid (&FfsFileHeader->Name, (EFI_GUID*)FileName)) {
238            *FileHeader = FfsFileHeader;
239            return EFI_SUCCESS;
240          }
241        } else if (((SearchType == FfsFileHeader->Type) || (SearchType == EFI_FV_FILETYPE_ALL)) &&
242                   (FfsFileHeader->Type != EFI_FV_FILETYPE_FFS_PAD)) {
243          *FileHeader = FfsFileHeader;
244          return EFI_SUCCESS;
245        }
246  
247        FileOffset += FileOccupiedSize;
248        FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + FileOccupiedSize);
249        break;
250  
251      case EFI_FILE_DELETED:
252        FileLength = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF;
253        FileOccupiedSize = GET_OCCUPIED_SIZE(FileLength, 8);
254        FileOffset += FileOccupiedSize;
255        FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + FileOccupiedSize);
256        break;
257  
258      default:
259        *FileHeader = NULL;
260        return EFI_NOT_FOUND;
261      }
262    }
263  
264  
265    *FileHeader = NULL;
266    return EFI_NOT_FOUND;
267  }
268  

这个函数实现还挺麻烦,起始主要就是解析Fv格式。计算要找的Fv或各种类型的文件在哪个位置。

主要依据:

  • Fv头信息,(EFI_FIRMWARE_VOLUME_HEADER *)FvHandle。 包括:FvLength,FvLength, ExtHeaderOffset。
  • 每个文件头信息,(EFI_FFS_FILE_HEADER **)FileHandle。包括: Size和Type.

我们可以通过Fv解析工具辅助分析Fv的组织结构:

VolInfo …/FVMAIN_COMPACT.Fv

4. FfsProcessFvFile 处理找到的Fv

//EmbeddedPkg/Library/PrePiLib/FwVol.c?fi=FfsProcessFvFile
789  EFI_STATUS
790  EFIAPI
791  FfsProcessFvFile (
792    IN  EFI_PEI_FILE_HANDLE   FvFileHandle
793    )
    ...
    820    //
821    // Find FvImage in FvFile
822    //
823    Status = FfsFindSectionData (EFI_SECTION_FIRMWARE_VOLUME_IMAGE, FvFileHandle, (VOID **)&FvImageHandle);
824    if (EFI_ERROR (Status)) {
825      return Status;
826    }
827  
    ...
859    //
860    // Inform HOB consumer phase, i.e. DXE core, the existence of this FV
861    //
862    BuildFvHob ((EFI_PHYSICAL_ADDRESS) (UINTN) FvImageInfo.FvStart, FvImageInfo.FvSize);
863  
864    //
865    // Makes the encapsulated volume show up in DXE phase to skip processing of
866    // encapsulated file again.
867    //
868    BuildFv2Hob (
869      (EFI_PHYSICAL_ADDRESS) (UINTN) FvImageInfo.FvStart,
870      FvImageInfo.FvSize,
871      &FvImageInfo.FvName,
872      &(((EFI_FFS_FILE_HEADER *)FvFileHandle)->Name)
873      );
874      

主要内容包括:

  • 检查EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE类型的Fv文件已经被处理了,即已经放在了HobFv2 List.如果已经处理,直接返回,被必要再处理了。
  • 在先前找到的Fv文件中,找出EFI_SECTION_FIRMWARE_VOLUME_IMAGE类型的FvImage。
  • 计算 FvImage 信息。
  • 检查FvImage。如果 FvImage所在的Buffer没有对齐,重新分别配一个对齐的Buffer.
  • 使用 FvImage的信息,创建Fv Hob List.
  • 使用 FvImage的信息,创建Fv2 Hob List…

8.当前问题

从压缩Fv中找出第2个未压缩的Fv信息,出错。

怀疑的点:

  • 和对齐有关。
  • 使用FindFileEx函数解析的信息不对。

其实,是研究的方向偏了。不需要这么复杂。只需要做一件事就成功了:将新添加的TEST_FV 地址和SIZE信息放在Hob就可以了。

9.解決问题

如何构建,TEST_FV 这个新的Fv的HOB呢?

调用BuildFvHob(FvBase, FvSize)函数即可。那么新增Fv的地址FvBase和FvSize怎么知道呢?

其实压根不需要从压缩的Fv中去解析TEST_FV所在地址。我们直接在FDF中划分一块Fv内存区域,显示的设定该Fv区域就行了。到时候PEI通过Pcd就能得到该新Fv的地址和Size。

26 [FD.EDKII]
27 BaseAddress   = 0x9F000000|gArmTokenSpaceGuid.PcdFdBaseAddress
28 Size          = 0x00280000|gArmTokenSpaceGuid.PcdFdSize
29 ErasePolarity = 1
30 
31 BlockSize     = 0x00001000|gSprdTokenSpaceGuid.PcdFirmwareBlockSize
32 NumBlocks     = 0x280
33 

59 0x00000000|0x00200000
60 gArmTokenSpaceGuid.PcdFvBaseAddress|gArmTokenSpaceGuid.PcdFvSize
61 FV = FVMAIN_COMPACT
62 
59 0x00200000|0x00080000
60 gSprdTokenSpaceGuid.PcdFv2BaseAddress|gSprdTokenSpaceGuid.PcdFv2Size
61 FV = FVTEST
62 

在Dec文件中声明这两个Pcd:

  • gSprdTokenSpaceGuid.PcdFv2BaseAddress, TEST.Fv在内存空间的基地址

  • gSprdTokenSpaceGuid.PcdFv2Size,TEST.Fv的Size

在这里插入图片描述

10.注意事项

  1. 新增Fv会导致FD 文件变大。新增加的Fv的Size,就是FD增加的Size。
  2. 新增Fv会导致内存空间不够,或者栈溢出。因为新增的Fv会被加载到其实际的物理地址空间。
  3. 新增加的Fv也是和其他Fv,以及栈,放在UEFI Region的,因此需要该空间足够大。否则,整个FD加载失败,或栈溢出,或某个模块加载失败。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老衲不依

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值