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