实现卷标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的固件卷服务中的实现。
-
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 函数。
-
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
-
先从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 }
-
为找到的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 //
-
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.注意事项
- 新增Fv会导致FD 文件变大。新增加的Fv的Size,就是FD增加的Size。
- 新增Fv会导致内存空间不够,或者栈溢出。因为新增的Fv会被加载到其实际的物理地址空间。
- 新增加的Fv也是和其他Fv,以及栈,放在UEFI Region的,因此需要该空间足够大。否则,整个FD加载失败,或栈溢出,或某个模块加载失败。