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