]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Dxe/FwVol/FwVol.c
9355e52ab068179760e6e19bcbc2ec6b7564cb8e
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / FwVol / FwVol.c
1 /** @file
2 Firmware File System driver that produce Firmware Volume protocol.
3 Layers on top of Firmware Block protocol to produce a file abstraction
4 of FV based files.
5
6 Copyright (c) 2006 - 2012, 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
11
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.
14
15 **/
16
17 #include "DxeMain.h"
18 #include "FwVolDriver.h"
19
20
21 //
22 // Protocol notify related globals
23 //
24 VOID *gEfiFwVolBlockNotifyReg;
25 EFI_EVENT gEfiFwVolBlockEvent;
26
27 FV_DEVICE mFvDevice = {
28 FV2_DEVICE_SIGNATURE,
29 NULL,
30 NULL,
31 {
32 FvGetVolumeAttributes,
33 FvSetVolumeAttributes,
34 FvReadFile,
35 FvReadFileSection,
36 FvWriteFile,
37 FvGetNextFile,
38 sizeof (UINTN),
39 NULL,
40 FvGetVolumeInfo,
41 FvSetVolumeInfo
42 },
43 NULL,
44 NULL,
45 NULL,
46 NULL,
47 { NULL, NULL },
48 0,
49 FALSE,
50 0
51 };
52
53
54 //
55 // FFS helper functions
56 //
57 /**
58 Read data from Firmware Block by FVB protocol Read.
59 The data may cross the multi block ranges.
60
61 @param Fvb The FW_VOL_BLOCK_PROTOCOL instance from which to read data.
62 @param StartLba Pointer to StartLba.
63 On input, the start logical block index from which to read.
64 On output,the end logical block index after reading.
65 @param Offset Pointer to Offset
66 On input, offset into the block at which to begin reading.
67 On output, offset into the end block after reading.
68 @param DataSize Size of data to be read.
69 @param Data Pointer to Buffer that the data will be read into.
70
71 @retval EFI_SUCCESS Successfully read data from firmware block.
72 @retval others
73 **/
74 EFI_STATUS
75 ReadFvbData (
76 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb,
77 IN OUT EFI_LBA *StartLba,
78 IN OUT UINTN *Offset,
79 IN UINTN DataSize,
80 OUT UINT8 *Data
81 )
82 {
83 UINTN BlockSize;
84 UINTN NumberOfBlocks;
85 UINTN BlockIndex;
86 UINTN ReadDataSize;
87 EFI_STATUS Status;
88
89 //
90 // Try read data in current block
91 //
92 BlockIndex = 0;
93 ReadDataSize = DataSize;
94 Status = Fvb->Read (Fvb, *StartLba, *Offset, &ReadDataSize, Data);
95 if (Status == EFI_SUCCESS) {
96 *Offset += DataSize;
97 return EFI_SUCCESS;
98 } else if (Status != EFI_BAD_BUFFER_SIZE) {
99 //
100 // other error will direct return
101 //
102 return Status;
103 }
104
105 //
106 // Data crosses the blocks, read data from next block
107 //
108 DataSize -= ReadDataSize;
109 Data += ReadDataSize;
110 *StartLba = *StartLba + 1;
111 while (DataSize > 0) {
112 Status = Fvb->GetBlockSize (Fvb, *StartLba, &BlockSize, &NumberOfBlocks);
113 if (EFI_ERROR (Status)) {
114 return Status;
115 }
116
117 //
118 // Read data from the crossing blocks
119 //
120 BlockIndex = 0;
121 while (BlockIndex < NumberOfBlocks && DataSize >= BlockSize) {
122 Status = Fvb->Read (Fvb, *StartLba + BlockIndex, 0, &BlockSize, Data);
123 if (EFI_ERROR (Status)) {
124 return Status;
125 }
126 Data += BlockSize;
127 DataSize -= BlockSize;
128 BlockIndex ++;
129 }
130
131 //
132 // Data doesn't exceed the current block range.
133 //
134 if (DataSize < BlockSize) {
135 break;
136 }
137
138 //
139 // Data must be got from the next block range.
140 //
141 *StartLba += NumberOfBlocks;
142 }
143
144 //
145 // read the remaining data
146 //
147 if (DataSize > 0) {
148 Status = Fvb->Read (Fvb, *StartLba + BlockIndex, 0, &DataSize, Data);
149 if (EFI_ERROR (Status)) {
150 return Status;
151 }
152 }
153
154 //
155 // Update Lba and Offset used by the following read.
156 //
157 *StartLba += BlockIndex;
158 *Offset = DataSize;
159
160 return EFI_SUCCESS;
161 }
162
163 /**
164 Given the supplied FW_VOL_BLOCK_PROTOCOL, allocate a buffer for output and
165 copy the real length volume header into it.
166
167 @param Fvb The FW_VOL_BLOCK_PROTOCOL instance from which to
168 read the volume header
169 @param FwVolHeader Pointer to pointer to allocated buffer in which
170 the volume header is returned.
171
172 @retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated.
173 @retval EFI_SUCCESS Successfully read volume header to the allocated
174 buffer.
175
176 **/
177 EFI_STATUS
178 GetFwVolHeader (
179 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb,
180 OUT EFI_FIRMWARE_VOLUME_HEADER **FwVolHeader
181 )
182 {
183 EFI_STATUS Status;
184 EFI_FIRMWARE_VOLUME_HEADER TempFvh;
185 UINTN FvhLength;
186 EFI_LBA StartLba;
187 UINTN Offset;
188 UINT8 *Buffer;
189
190 //
191 // Read the standard FV header
192 //
193 StartLba = 0;
194 Offset = 0;
195 FvhLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER);
196 Status = ReadFvbData (Fvb, &StartLba, &Offset, FvhLength, (UINT8 *)&TempFvh);
197 if (EFI_ERROR (Status)) {
198 return Status;
199 }
200
201 //
202 // Allocate a buffer for the caller
203 //
204 *FwVolHeader = AllocatePool (TempFvh.HeaderLength);
205 if (*FwVolHeader == NULL) {
206 return EFI_OUT_OF_RESOURCES;
207 }
208
209 //
210 // Copy the standard header into the buffer
211 //
212 CopyMem (*FwVolHeader, &TempFvh, sizeof (EFI_FIRMWARE_VOLUME_HEADER));
213
214 //
215 // Read the rest of the header
216 //
217 FvhLength = TempFvh.HeaderLength - sizeof (EFI_FIRMWARE_VOLUME_HEADER);
218 Buffer = (UINT8 *)*FwVolHeader + sizeof (EFI_FIRMWARE_VOLUME_HEADER);
219 Status = ReadFvbData (Fvb, &StartLba, &Offset, FvhLength, Buffer);
220 if (EFI_ERROR (Status)) {
221 //
222 // Read failed so free buffer
223 //
224 CoreFreePool (*FwVolHeader);
225 }
226
227 return Status;
228 }
229
230
231
232 /**
233 Free FvDevice resource when error happens
234
235 @param FvDevice pointer to the FvDevice to be freed.
236
237 **/
238 VOID
239 FreeFvDeviceResource (
240 IN FV_DEVICE *FvDevice
241 )
242 {
243 FFS_FILE_LIST_ENTRY *FfsFileEntry;
244 LIST_ENTRY *NextEntry;
245
246 //
247 // Free File List Entry
248 //
249 FfsFileEntry = (FFS_FILE_LIST_ENTRY *)FvDevice->FfsFileListHeader.ForwardLink;
250 while (&FfsFileEntry->Link != &FvDevice->FfsFileListHeader) {
251 NextEntry = (&FfsFileEntry->Link)->ForwardLink;
252
253 if (FfsFileEntry->StreamHandle != 0) {
254 //
255 // Close stream and free resources from SEP
256 //
257 CloseSectionStream (FfsFileEntry->StreamHandle);
258 }
259
260 CoreFreePool (FfsFileEntry);
261
262 FfsFileEntry = (FFS_FILE_LIST_ENTRY *) NextEntry;
263 }
264
265
266 //
267 // Free the cache
268 //
269 CoreFreePool (FvDevice->CachedFv);
270
271 //
272 // Free Volume Header
273 //
274 CoreFreePool (FvDevice->FwVolHeader);
275
276 return;
277 }
278
279
280
281 /**
282 Check if an FV is consistent and allocate cache for it.
283
284 @param FvDevice A pointer to the FvDevice to be checked.
285
286 @retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated.
287 @retval EFI_SUCCESS FV is consistent and cache is allocated.
288 @retval EFI_VOLUME_CORRUPTED File system is corrupted.
289
290 **/
291 EFI_STATUS
292 FvCheck (
293 IN OUT FV_DEVICE *FvDevice
294 )
295 {
296 EFI_STATUS Status;
297 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
298 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
299 EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExtHeader;
300 EFI_FVB_ATTRIBUTES_2 FvbAttributes;
301 EFI_FV_BLOCK_MAP_ENTRY *BlockMap;
302 FFS_FILE_LIST_ENTRY *FfsFileEntry;
303 EFI_FFS_FILE_HEADER *FfsHeader;
304 UINT8 *CacheLocation;
305 UINTN LbaOffset;
306 UINTN HeaderSize;
307 UINTN Index;
308 EFI_LBA LbaIndex;
309 UINTN Size;
310 EFI_FFS_FILE_STATE FileState;
311 UINT8 *TopFvAddress;
312 UINTN TestLength;
313
314
315 Fvb = FvDevice->Fvb;
316 FwVolHeader = FvDevice->FwVolHeader;
317
318 Status = Fvb->GetAttributes (Fvb, &FvbAttributes);
319 if (EFI_ERROR (Status)) {
320 return Status;
321 }
322
323 //
324 // Size is the size of the FV minus the head. We have already allocated
325 // the header to check to make sure the volume is valid
326 //
327 Size = (UINTN)(FwVolHeader->FvLength - FwVolHeader->HeaderLength);
328 FvDevice->CachedFv = AllocatePool (Size);
329
330 if (FvDevice->CachedFv == NULL) {
331 return EFI_OUT_OF_RESOURCES;
332 }
333
334 //
335 // Remember a pointer to the end fo the CachedFv
336 //
337 FvDevice->EndOfCachedFv = FvDevice->CachedFv + Size;
338
339 //
340 // Copy FV minus header into memory using the block map we have all ready
341 // read into memory.
342 //
343 BlockMap = FwVolHeader->BlockMap;
344 CacheLocation = FvDevice->CachedFv;
345 LbaIndex = 0;
346 LbaOffset = 0;
347 HeaderSize = FwVolHeader->HeaderLength;
348 while ((BlockMap->NumBlocks != 0) || (BlockMap->Length != 0)) {
349 Index = 0;
350 Size = BlockMap->Length;
351 if (HeaderSize > 0) {
352 //
353 // Skip header size
354 //
355 for (; Index < BlockMap->NumBlocks && HeaderSize >= BlockMap->Length; Index ++) {
356 HeaderSize -= BlockMap->Length;
357 LbaIndex ++;
358 }
359
360 //
361 // Check whether FvHeader is crossing the multi block range.
362 //
363 if (Index >= BlockMap->NumBlocks) {
364 BlockMap++;
365 continue;
366 } else if (HeaderSize > 0) {
367 LbaOffset = HeaderSize;
368 Size = BlockMap->Length - HeaderSize;
369 HeaderSize = 0;
370 }
371 }
372
373 //
374 // read the FV data
375 //
376 for (; Index < BlockMap->NumBlocks; Index ++) {
377 Status = Fvb->Read (Fvb,
378 LbaIndex,
379 LbaOffset,
380 &Size,
381 CacheLocation
382 );
383
384 //
385 // Not check EFI_BAD_BUFFER_SIZE, for Size = BlockMap->Length
386 //
387 if (EFI_ERROR (Status)) {
388 goto Done;
389 }
390
391 LbaIndex++;
392 CacheLocation += Size;
393
394 //
395 // After we skip Fv Header always read from start of block
396 //
397 LbaOffset = 0;
398 Size = BlockMap->Length;
399 }
400
401 BlockMap++;
402 }
403
404 //
405 // Scan to check the free space & File list
406 //
407 if ((FvbAttributes & EFI_FVB2_ERASE_POLARITY) != 0) {
408 FvDevice->ErasePolarity = 1;
409 } else {
410 FvDevice->ErasePolarity = 0;
411 }
412
413
414 //
415 // go through the whole FV cache, check the consistence of the FV.
416 // Make a linked list of all the Ffs file headers
417 //
418 Status = EFI_SUCCESS;
419 InitializeListHead (&FvDevice->FfsFileListHeader);
420
421 //
422 // Build FFS list
423 //
424 if (FwVolHeader->ExtHeaderOffset != 0) {
425 //
426 // Searching for files starts on an 8 byte aligned boundary after the end of the Extended Header if it exists.
427 //
428 FwVolExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *) (FvDevice->CachedFv + (FwVolHeader->ExtHeaderOffset - FwVolHeader->HeaderLength));
429 FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FwVolExtHeader + FwVolExtHeader->ExtHeaderSize);
430 FfsHeader = (EFI_FFS_FILE_HEADER *) ALIGN_POINTER (FfsHeader, 8);
431 } else {
432 FfsHeader = (EFI_FFS_FILE_HEADER *) (FvDevice->CachedFv);
433 }
434 TopFvAddress = FvDevice->EndOfCachedFv;
435 while ((UINT8 *) FfsHeader < TopFvAddress) {
436
437 TestLength = TopFvAddress - ((UINT8 *) FfsHeader);
438 if (TestLength > sizeof (EFI_FFS_FILE_HEADER)) {
439 TestLength = sizeof (EFI_FFS_FILE_HEADER);
440 }
441
442 if (IsBufferErased (FvDevice->ErasePolarity, FfsHeader, TestLength)) {
443 //
444 // We have found the free space so we are done!
445 //
446 goto Done;
447 }
448
449 if (!IsValidFfsHeader (FvDevice->ErasePolarity, FfsHeader, &FileState)) {
450 if ((FileState == EFI_FILE_HEADER_INVALID) ||
451 (FileState == EFI_FILE_HEADER_CONSTRUCTION)) {
452 if (IS_FFS_FILE2 (FfsHeader)) {
453 if (!FvDevice->IsFfs3Fv) {
454 DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &FfsHeader->Name));
455 }
456 FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2));
457 } else {
458 FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER));
459 }
460 continue;
461 } else {
462 //
463 // File system is corrputed
464 //
465 Status = EFI_VOLUME_CORRUPTED;
466 goto Done;
467 }
468 }
469
470 if (!IsValidFfsFile (FvDevice->ErasePolarity, FfsHeader)) {
471 //
472 // File system is corrupted
473 //
474 Status = EFI_VOLUME_CORRUPTED;
475 goto Done;
476 }
477
478 if (IS_FFS_FILE2 (FfsHeader)) {
479 ASSERT (FFS_FILE2_SIZE (FfsHeader) > 0x00FFFFFF);
480 if (!FvDevice->IsFfs3Fv) {
481 DEBUG ((EFI_D_ERROR, "Found a FFS3 formatted file: %g in a non-FFS3 formatted FV.\n", &FfsHeader->Name));
482 FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE2_SIZE (FfsHeader));
483 //
484 // Adjust pointer to the next 8-byte aligned boundry.
485 //
486 FfsHeader = (EFI_FFS_FILE_HEADER *) (((UINTN) FfsHeader + 7) & ~0x07);
487 continue;
488 }
489 }
490
491 FileState = GetFileState (FvDevice->ErasePolarity, FfsHeader);
492
493 //
494 // check for non-deleted file
495 //
496 if (FileState != EFI_FILE_DELETED) {
497 //
498 // Create a FFS list entry for each non-deleted file
499 //
500 FfsFileEntry = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY));
501 if (FfsFileEntry == NULL) {
502 Status = EFI_OUT_OF_RESOURCES;
503 goto Done;
504 }
505
506 FfsFileEntry->FfsHeader = FfsHeader;
507 InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link);
508 }
509
510 if (IS_FFS_FILE2 (FfsHeader)) {
511 FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE2_SIZE (FfsHeader));
512 } else {
513 FfsHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsHeader + FFS_FILE_SIZE (FfsHeader));
514 }
515
516 //
517 // Adjust pointer to the next 8-byte aligned boundry.
518 //
519 FfsHeader = (EFI_FFS_FILE_HEADER *)(((UINTN)FfsHeader + 7) & ~0x07);
520
521 }
522
523 Done:
524 if (EFI_ERROR (Status)) {
525 FreeFvDeviceResource (FvDevice);
526 }
527
528 return Status;
529 }
530
531
532
533 /**
534 This notification function is invoked when an instance of the
535 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL is produced. It layers an instance of the
536 EFI_FIRMWARE_VOLUME2_PROTOCOL on the same handle. This is the function where
537 the actual initialization of the EFI_FIRMWARE_VOLUME2_PROTOCOL is done.
538
539 @param Event The event that occured
540 @param Context For EFI compatiblity. Not used.
541
542 **/
543 VOID
544 EFIAPI
545 NotifyFwVolBlock (
546 IN EFI_EVENT Event,
547 IN VOID *Context
548 )
549 {
550 EFI_HANDLE Handle;
551 EFI_STATUS Status;
552 UINTN BufferSize;
553 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
554 EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
555 FV_DEVICE *FvDevice;
556 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
557 //
558 // Examine all new handles
559 //
560 for (;;) {
561 //
562 // Get the next handle
563 //
564 BufferSize = sizeof (Handle);
565 Status = CoreLocateHandle (
566 ByRegisterNotify,
567 NULL,
568 gEfiFwVolBlockNotifyReg,
569 &BufferSize,
570 &Handle
571 );
572
573 //
574 // If not found, we're done
575 //
576 if (EFI_NOT_FOUND == Status) {
577 break;
578 }
579
580 if (EFI_ERROR (Status)) {
581 continue;
582 }
583
584 //
585 // Get the FirmwareVolumeBlock protocol on that handle
586 //
587 Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
588 ASSERT_EFI_ERROR (Status);
589 ASSERT (Fvb != NULL);
590
591 //
592 // Make sure the Fv Header is O.K.
593 //
594 Status = GetFwVolHeader (Fvb, &FwVolHeader);
595 if (EFI_ERROR (Status)) {
596 return;
597 }
598 ASSERT (FwVolHeader != NULL);
599
600 if (!VerifyFvHeaderChecksum (FwVolHeader)) {
601 CoreFreePool (FwVolHeader);
602 continue;
603 }
604
605
606 //
607 // Check to see that the file system is indeed formatted in a way we can
608 // understand it...
609 //
610 if ((!CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem2Guid)) &&
611 (!CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem3Guid))) {
612 continue;
613 }
614
615 //
616 // Check if there is an FV protocol already installed in that handle
617 //
618 Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv);
619 if (!EFI_ERROR (Status)) {
620 //
621 // Update Fv to use a new Fvb
622 //
623 FvDevice = BASE_CR (Fv, FV_DEVICE, Fv);
624 if (FvDevice->Signature == FV2_DEVICE_SIGNATURE) {
625 //
626 // Only write into our device structure if it's our device structure
627 //
628 FvDevice->Fvb = Fvb;
629 }
630
631 } else {
632 //
633 // No FwVol protocol on the handle so create a new one
634 //
635 FvDevice = AllocateCopyPool (sizeof (FV_DEVICE), &mFvDevice);
636 if (FvDevice == NULL) {
637 return;
638 }
639
640 FvDevice->Fvb = Fvb;
641 FvDevice->Handle = Handle;
642 FvDevice->FwVolHeader = FwVolHeader;
643 FvDevice->IsFfs3Fv = CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem3Guid);
644 FvDevice->Fv.ParentHandle = Fvb->ParentHandle;
645
646 if (Fvb->ParentHandle != NULL) {
647 //
648 // Inherit the authentication status from FVB.
649 //
650 FvDevice->AuthenticationStatus = GetFvbAuthenticationStatus (Fvb);
651 }
652
653 if (!EFI_ERROR (FvCheck (FvDevice))) {
654 //
655 // Install an New FV protocol on the existing handle
656 //
657 Status = CoreInstallProtocolInterface (
658 &Handle,
659 &gEfiFirmwareVolume2ProtocolGuid,
660 EFI_NATIVE_INTERFACE,
661 &FvDevice->Fv
662 );
663 ASSERT_EFI_ERROR (Status);
664 } else {
665 //
666 // Free FvDevice Buffer for the corrupt FV image.
667 //
668 CoreFreePool (FvDevice);
669 }
670 }
671 }
672
673 return;
674 }
675
676
677
678 /**
679 This routine is the driver initialization entry point. It registers
680 a notification function. This notification function are responsible
681 for building the FV stack dynamically.
682
683 @param ImageHandle The image handle.
684 @param SystemTable The system table.
685
686 @retval EFI_SUCCESS Function successfully returned.
687
688 **/
689 EFI_STATUS
690 EFIAPI
691 FwVolDriverInit (
692 IN EFI_HANDLE ImageHandle,
693 IN EFI_SYSTEM_TABLE *SystemTable
694 )
695 {
696 gEfiFwVolBlockEvent = EfiCreateProtocolNotifyEvent (
697 &gEfiFirmwareVolumeBlockProtocolGuid,
698 TPL_CALLBACK,
699 NotifyFwVolBlock,
700 NULL,
701 &gEfiFwVolBlockNotifyReg
702 );
703 return EFI_SUCCESS;
704 }
705
706