2 Firmware File System driver that produce Firmware Volume protocol.
3 Layers on top of Firmware Block protocol to produce a file abstraction
6 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include "FwVolDriver.h"
22 // Protocol notify related globals
24 VOID
*gEfiFwVolBlockNotifyReg
;
25 EFI_EVENT gEfiFwVolBlockEvent
;
27 FV_DEVICE mFvDevice
= {
32 FvGetVolumeAttributes
,
33 FvSetVolumeAttributes
,
56 // FFS helper functions
59 Read data from Firmware Block by FVB protocol Read.
60 The data may cross the multi block ranges.
62 @param Fvb The FW_VOL_BLOCK_PROTOCOL instance from which to read data.
63 @param StartLba Pointer to StartLba.
64 On input, the start logical block index from which to read.
65 On output,the end logical block index after reading.
66 @param Offset Pointer to Offset
67 On input, offset into the block at which to begin reading.
68 On output, offset into the end block after reading.
69 @param DataSize Size of data to be read.
70 @param Data Pointer to Buffer that the data will be read into.
72 @retval EFI_SUCCESS Successfully read data from firmware block.
77 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
,
78 IN OUT EFI_LBA
*StartLba
,
91 // Try read data in current block
94 ReadDataSize
= DataSize
;
95 Status
= Fvb
->Read (Fvb
, *StartLba
, *Offset
, &ReadDataSize
, Data
);
96 if (Status
== EFI_SUCCESS
) {
99 } else if (Status
!= EFI_BAD_BUFFER_SIZE
) {
101 // other error will direct return
107 // Data crosses the blocks, read data from next block
109 DataSize
-= ReadDataSize
;
110 Data
+= ReadDataSize
;
111 *StartLba
= *StartLba
+ 1;
112 while (DataSize
> 0) {
113 Status
= Fvb
->GetBlockSize (Fvb
, *StartLba
, &BlockSize
, &NumberOfBlocks
);
114 if (EFI_ERROR (Status
)) {
119 // Read data from the crossing blocks
122 while (BlockIndex
< NumberOfBlocks
&& DataSize
>= BlockSize
) {
123 Status
= Fvb
->Read (Fvb
, *StartLba
+ BlockIndex
, 0, &BlockSize
, Data
);
124 if (EFI_ERROR (Status
)) {
128 DataSize
-= BlockSize
;
133 // Data doesn't exceed the current block range.
135 if (DataSize
< BlockSize
) {
140 // Data must be got from the next block range.
142 *StartLba
+= NumberOfBlocks
;
146 // read the remaining data
149 Status
= Fvb
->Read (Fvb
, *StartLba
+ BlockIndex
, 0, &DataSize
, Data
);
150 if (EFI_ERROR (Status
)) {
156 // Update Lba and Offset used by the following read.
158 *StartLba
+= BlockIndex
;
165 Given the supplied FW_VOL_BLOCK_PROTOCOL, allocate a buffer for output and
166 copy the real length volume header into it.
168 @param Fvb The FW_VOL_BLOCK_PROTOCOL instance from which to
169 read the volume header
170 @param FwVolHeader Pointer to pointer to allocated buffer in which
171 the volume header is returned.
173 @retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated.
174 @retval EFI_SUCCESS Successfully read volume header to the allocated
176 @retval EFI_INVALID_PARAMETER The FV Header signature is not as expected or
177 the file system could not be understood.
182 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
,
183 OUT EFI_FIRMWARE_VOLUME_HEADER
**FwVolHeader
187 EFI_FIRMWARE_VOLUME_HEADER TempFvh
;
194 // Read the standard FV header
198 FvhLength
= sizeof (EFI_FIRMWARE_VOLUME_HEADER
);
199 Status
= ReadFvbData (Fvb
, &StartLba
, &Offset
, FvhLength
, (UINT8
*)&TempFvh
);
200 if (EFI_ERROR (Status
)) {
205 // Validate FV Header signature, if not as expected, continue.
207 if (TempFvh
.Signature
!= EFI_FVH_SIGNATURE
) {
208 return EFI_INVALID_PARAMETER
;
212 // Check to see that the file system is indeed formatted in a way we can
215 if ((!CompareGuid (&TempFvh
.FileSystemGuid
, &gEfiFirmwareFileSystem2Guid
)) &&
216 (!CompareGuid (&TempFvh
.FileSystemGuid
, &gEfiFirmwareFileSystem3Guid
))) {
217 return EFI_INVALID_PARAMETER
;
221 // Allocate a buffer for the caller
223 *FwVolHeader
= AllocatePool (TempFvh
.HeaderLength
);
224 if (*FwVolHeader
== NULL
) {
225 return EFI_OUT_OF_RESOURCES
;
229 // Copy the standard header into the buffer
231 CopyMem (*FwVolHeader
, &TempFvh
, sizeof (EFI_FIRMWARE_VOLUME_HEADER
));
234 // Read the rest of the header
236 FvhLength
= TempFvh
.HeaderLength
- sizeof (EFI_FIRMWARE_VOLUME_HEADER
);
237 Buffer
= (UINT8
*)*FwVolHeader
+ sizeof (EFI_FIRMWARE_VOLUME_HEADER
);
238 Status
= ReadFvbData (Fvb
, &StartLba
, &Offset
, FvhLength
, Buffer
);
239 if (EFI_ERROR (Status
)) {
241 // Read failed so free buffer
243 CoreFreePool (*FwVolHeader
);
252 Free FvDevice resource when error happens
254 @param FvDevice pointer to the FvDevice to be freed.
258 FreeFvDeviceResource (
259 IN FV_DEVICE
*FvDevice
262 FFS_FILE_LIST_ENTRY
*FfsFileEntry
;
263 LIST_ENTRY
*NextEntry
;
266 // Free File List Entry
268 FfsFileEntry
= (FFS_FILE_LIST_ENTRY
*)FvDevice
->FfsFileListHeader
.ForwardLink
;
269 while (&FfsFileEntry
->Link
!= &FvDevice
->FfsFileListHeader
) {
270 NextEntry
= (&FfsFileEntry
->Link
)->ForwardLink
;
272 if (FfsFileEntry
->StreamHandle
!= 0) {
274 // Close stream and free resources from SEP
276 CloseSectionStream (FfsFileEntry
->StreamHandle
, FALSE
);
279 if (FfsFileEntry
->FileCached
) {
281 // Free the cached file buffer.
283 CoreFreePool (FfsFileEntry
->FfsHeader
);
286 CoreFreePool (FfsFileEntry
);
288 FfsFileEntry
= (FFS_FILE_LIST_ENTRY
*) NextEntry
;
291 if (!FvDevice
->IsMemoryMapped
) {
293 // Free the cached FV buffer.
295 CoreFreePool (FvDevice
->CachedFv
);
299 // Free Volume Header
301 CoreFreePool (FvDevice
->FwVolHeader
);
309 Check if an FV is consistent and allocate cache for it.
311 @param FvDevice A pointer to the FvDevice to be checked.
313 @retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated.
314 @retval EFI_SUCCESS FV is consistent and cache is allocated.
315 @retval EFI_VOLUME_CORRUPTED File system is corrupted.
320 IN OUT FV_DEVICE
*FvDevice
324 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
325 EFI_FIRMWARE_VOLUME_HEADER
*FwVolHeader
;
326 EFI_FIRMWARE_VOLUME_EXT_HEADER
*FwVolExtHeader
;
327 EFI_FVB_ATTRIBUTES_2 FvbAttributes
;
328 EFI_FV_BLOCK_MAP_ENTRY
*BlockMap
;
329 FFS_FILE_LIST_ENTRY
*FfsFileEntry
;
330 EFI_FFS_FILE_HEADER
*FfsHeader
;
331 UINT8
*CacheLocation
;
337 EFI_FFS_FILE_STATE FileState
;
340 EFI_PHYSICAL_ADDRESS PhysicalAddress
;
343 EFI_FFS_FILE_HEADER
*CacheFfsHeader
;
346 CacheFfsHeader
= NULL
;
349 FwVolHeader
= FvDevice
->FwVolHeader
;
351 Status
= Fvb
->GetAttributes (Fvb
, &FvbAttributes
);
352 if (EFI_ERROR (Status
)) {
357 // Size is the size of the FV minus the head. We have already allocated
358 // the header to check to make sure the volume is valid
360 Size
= (UINTN
)(FwVolHeader
->FvLength
- FwVolHeader
->HeaderLength
);
361 if ((FvbAttributes
& EFI_FVB2_MEMORY_MAPPED
) != 0) {
362 FvDevice
->IsMemoryMapped
= TRUE
;
364 Status
= Fvb
->GetPhysicalAddress (Fvb
, &PhysicalAddress
);
365 if (EFI_ERROR (Status
)) {
370 // Don't cache memory mapped FV really.
372 FvDevice
->CachedFv
= (UINT8
*) (UINTN
) (PhysicalAddress
+ FwVolHeader
->HeaderLength
);
374 FvDevice
->IsMemoryMapped
= FALSE
;
375 FvDevice
->CachedFv
= AllocatePool (Size
);
377 if (FvDevice
->CachedFv
== NULL
) {
378 return EFI_OUT_OF_RESOURCES
;
383 // Remember a pointer to the end fo the CachedFv
385 FvDevice
->EndOfCachedFv
= FvDevice
->CachedFv
+ Size
;
387 if (!FvDevice
->IsMemoryMapped
) {
389 // Copy FV minus header into memory using the block map we have all ready
392 BlockMap
= FwVolHeader
->BlockMap
;
393 CacheLocation
= FvDevice
->CachedFv
;
396 HeaderSize
= FwVolHeader
->HeaderLength
;
397 while ((BlockMap
->NumBlocks
!= 0) || (BlockMap
->Length
!= 0)) {
399 Size
= BlockMap
->Length
;
400 if (HeaderSize
> 0) {
404 for (; Index
< BlockMap
->NumBlocks
&& HeaderSize
>= BlockMap
->Length
; Index
++) {
405 HeaderSize
-= BlockMap
->Length
;
410 // Check whether FvHeader is crossing the multi block range.
412 if (Index
>= BlockMap
->NumBlocks
) {
415 } else if (HeaderSize
> 0) {
416 LbaOffset
= HeaderSize
;
417 Size
= BlockMap
->Length
- HeaderSize
;
425 for (; Index
< BlockMap
->NumBlocks
; Index
++) {
426 Status
= Fvb
->Read (Fvb
,
434 // Not check EFI_BAD_BUFFER_SIZE, for Size = BlockMap->Length
436 if (EFI_ERROR (Status
)) {
441 CacheLocation
+= Size
;
444 // After we skip Fv Header always read from start of block
447 Size
= BlockMap
->Length
;
455 // Scan to check the free space & File list
457 if ((FvbAttributes
& EFI_FVB2_ERASE_POLARITY
) != 0) {
458 FvDevice
->ErasePolarity
= 1;
460 FvDevice
->ErasePolarity
= 0;
465 // go through the whole FV cache, check the consistence of the FV.
466 // Make a linked list of all the Ffs file headers
468 Status
= EFI_SUCCESS
;
469 InitializeListHead (&FvDevice
->FfsFileListHeader
);
474 if (FwVolHeader
->ExtHeaderOffset
!= 0) {
476 // Searching for files starts on an 8 byte aligned boundary after the end of the Extended Header if it exists.
478 FwVolExtHeader
= (EFI_FIRMWARE_VOLUME_EXT_HEADER
*) (FvDevice
->CachedFv
+ (FwVolHeader
->ExtHeaderOffset
- FwVolHeader
->HeaderLength
));
479 FfsHeader
= (EFI_FFS_FILE_HEADER
*) ((UINT8
*) FwVolExtHeader
+ FwVolExtHeader
->ExtHeaderSize
);
480 FfsHeader
= (EFI_FFS_FILE_HEADER
*) ALIGN_POINTER (FfsHeader
, 8);
482 FfsHeader
= (EFI_FFS_FILE_HEADER
*) (FvDevice
->CachedFv
);
484 TopFvAddress
= FvDevice
->EndOfCachedFv
;
485 while (((UINTN
) FfsHeader
>= (UINTN
) FvDevice
->CachedFv
) && ((UINTN
) FfsHeader
<= (UINTN
) ((UINTN
) TopFvAddress
- sizeof (EFI_FFS_FILE_HEADER
)))) {
488 CoreFreePool (CacheFfsHeader
);
492 TestLength
= TopFvAddress
- ((UINT8
*) FfsHeader
);
493 if (TestLength
> sizeof (EFI_FFS_FILE_HEADER
)) {
494 TestLength
= sizeof (EFI_FFS_FILE_HEADER
);
497 if (IsBufferErased (FvDevice
->ErasePolarity
, FfsHeader
, TestLength
)) {
499 // We have found the free space so we are done!
504 if (!IsValidFfsHeader (FvDevice
->ErasePolarity
, FfsHeader
, &FileState
)) {
505 if ((FileState
== EFI_FILE_HEADER_INVALID
) ||
506 (FileState
== EFI_FILE_HEADER_CONSTRUCTION
)) {
507 if (IS_FFS_FILE2 (FfsHeader
)) {
508 if (!FvDevice
->IsFfs3Fv
) {
509 DEBUG ((EFI_D_ERROR
, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &FfsHeader
->Name
));
511 FfsHeader
= (EFI_FFS_FILE_HEADER
*) ((UINT8
*) FfsHeader
+ sizeof (EFI_FFS_FILE_HEADER2
));
513 FfsHeader
= (EFI_FFS_FILE_HEADER
*) ((UINT8
*) FfsHeader
+ sizeof (EFI_FFS_FILE_HEADER
));
518 // File system is corrputed
520 Status
= EFI_VOLUME_CORRUPTED
;
525 CacheFfsHeader
= FfsHeader
;
526 if ((CacheFfsHeader
->Attributes
& FFS_ATTRIB_CHECKSUM
) == FFS_ATTRIB_CHECKSUM
) {
527 if (FvDevice
->IsMemoryMapped
) {
529 // Memory mapped FV has not been cached.
530 // Here is to cache FFS file to memory buffer for following checksum calculating.
531 // And then, the cached file buffer can be also used for FvReadFile.
533 WholeFileSize
= IS_FFS_FILE2 (CacheFfsHeader
) ? FFS_FILE2_SIZE (CacheFfsHeader
): FFS_FILE_SIZE (CacheFfsHeader
);
534 CacheFfsHeader
= AllocateCopyPool (WholeFileSize
, CacheFfsHeader
);
535 if (CacheFfsHeader
== NULL
) {
536 Status
= EFI_OUT_OF_RESOURCES
;
543 if (!IsValidFfsFile (FvDevice
->ErasePolarity
, CacheFfsHeader
)) {
545 // File system is corrupted
547 Status
= EFI_VOLUME_CORRUPTED
;
551 if (IS_FFS_FILE2 (CacheFfsHeader
)) {
552 ASSERT (FFS_FILE2_SIZE (CacheFfsHeader
) > 0x00FFFFFF);
553 if (!FvDevice
->IsFfs3Fv
) {
554 DEBUG ((EFI_D_ERROR
, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &CacheFfsHeader
->Name
));
555 FfsHeader
= (EFI_FFS_FILE_HEADER
*) ((UINT8
*) FfsHeader
+ FFS_FILE2_SIZE (CacheFfsHeader
));
557 // Adjust pointer to the next 8-byte aligned boundary.
559 FfsHeader
= (EFI_FFS_FILE_HEADER
*) (((UINTN
) FfsHeader
+ 7) & ~0x07);
564 FileState
= GetFileState (FvDevice
->ErasePolarity
, CacheFfsHeader
);
567 // check for non-deleted file
569 if (FileState
!= EFI_FILE_DELETED
) {
571 // Create a FFS list entry for each non-deleted file
573 FfsFileEntry
= AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY
));
574 if (FfsFileEntry
== NULL
) {
575 Status
= EFI_OUT_OF_RESOURCES
;
579 FfsFileEntry
->FfsHeader
= CacheFfsHeader
;
580 FfsFileEntry
->FileCached
= FileCached
;
582 InsertTailList (&FvDevice
->FfsFileListHeader
, &FfsFileEntry
->Link
);
585 if (IS_FFS_FILE2 (CacheFfsHeader
)) {
586 FfsHeader
= (EFI_FFS_FILE_HEADER
*) ((UINT8
*) FfsHeader
+ FFS_FILE2_SIZE (CacheFfsHeader
));
588 FfsHeader
= (EFI_FFS_FILE_HEADER
*) ((UINT8
*) FfsHeader
+ FFS_FILE_SIZE (CacheFfsHeader
));
592 // Adjust pointer to the next 8-byte aligned boundary.
594 FfsHeader
= (EFI_FFS_FILE_HEADER
*)(((UINTN
)FfsHeader
+ 7) & ~0x07);
599 if (EFI_ERROR (Status
)) {
601 CoreFreePool (CacheFfsHeader
);
604 FreeFvDeviceResource (FvDevice
);
613 This notification function is invoked when an instance of the
614 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL is produced. It layers an instance of the
615 EFI_FIRMWARE_VOLUME2_PROTOCOL on the same handle. This is the function where
616 the actual initialization of the EFI_FIRMWARE_VOLUME2_PROTOCOL is done.
618 @param Event The event that occured
619 @param Context For EFI compatiblity. Not used.
632 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
633 EFI_FIRMWARE_VOLUME2_PROTOCOL
*Fv
;
635 EFI_FIRMWARE_VOLUME_HEADER
*FwVolHeader
;
637 // Examine all new handles
641 // Get the next handle
643 BufferSize
= sizeof (Handle
);
644 Status
= CoreLocateHandle (
647 gEfiFwVolBlockNotifyReg
,
653 // If not found, we're done
655 if (EFI_NOT_FOUND
== Status
) {
659 if (EFI_ERROR (Status
)) {
664 // Get the FirmwareVolumeBlock protocol on that handle
666 Status
= CoreHandleProtocol (Handle
, &gEfiFirmwareVolumeBlockProtocolGuid
, (VOID
**)&Fvb
);
667 ASSERT_EFI_ERROR (Status
);
668 ASSERT (Fvb
!= NULL
);
671 // Make sure the Fv Header is O.K.
673 Status
= GetFwVolHeader (Fvb
, &FwVolHeader
);
674 if (EFI_ERROR (Status
)) {
677 ASSERT (FwVolHeader
!= NULL
);
679 if (!VerifyFvHeaderChecksum (FwVolHeader
)) {
680 CoreFreePool (FwVolHeader
);
685 // Check if there is an FV protocol already installed in that handle
687 Status
= CoreHandleProtocol (Handle
, &gEfiFirmwareVolume2ProtocolGuid
, (VOID
**)&Fv
);
688 if (!EFI_ERROR (Status
)) {
690 // Update Fv to use a new Fvb
692 FvDevice
= BASE_CR (Fv
, FV_DEVICE
, Fv
);
693 if (FvDevice
->Signature
== FV2_DEVICE_SIGNATURE
) {
695 // Only write into our device structure if it's our device structure
702 // No FwVol protocol on the handle so create a new one
704 FvDevice
= AllocateCopyPool (sizeof (FV_DEVICE
), &mFvDevice
);
705 if (FvDevice
== NULL
) {
710 FvDevice
->Handle
= Handle
;
711 FvDevice
->FwVolHeader
= FwVolHeader
;
712 FvDevice
->IsFfs3Fv
= CompareGuid (&FwVolHeader
->FileSystemGuid
, &gEfiFirmwareFileSystem3Guid
);
713 FvDevice
->Fv
.ParentHandle
= Fvb
->ParentHandle
;
715 if (Fvb
->ParentHandle
!= NULL
) {
717 // Inherit the authentication status from FVB.
719 FvDevice
->AuthenticationStatus
= GetFvbAuthenticationStatus (Fvb
);
722 if (!EFI_ERROR (FvCheck (FvDevice
))) {
724 // Install an New FV protocol on the existing handle
726 Status
= CoreInstallProtocolInterface (
728 &gEfiFirmwareVolume2ProtocolGuid
,
729 EFI_NATIVE_INTERFACE
,
732 ASSERT_EFI_ERROR (Status
);
735 // Free FvDevice Buffer for the corrupt FV image.
737 CoreFreePool (FvDevice
);
748 This routine is the driver initialization entry point. It registers
749 a notification function. This notification function are responsible
750 for building the FV stack dynamically.
752 @param ImageHandle The image handle.
753 @param SystemTable The system table.
755 @retval EFI_SUCCESS Function successfully returned.
761 IN EFI_HANDLE ImageHandle
,
762 IN EFI_SYSTEM_TABLE
*SystemTable
765 gEfiFwVolBlockEvent
= EfiCreateProtocolNotifyEvent (
766 &gEfiFirmwareVolumeBlockProtocolGuid
,
770 &gEfiFwVolBlockNotifyReg