]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Core/Dxe/FwVol/FwVol.c
Code Scrub for Dxe Core.
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / FwVol / FwVol.c
CommitLineData
162ed594 1/** @file\r
28a00297 2 Firmware File System driver that produce Firmware Volume protocol.\r
022c6d45 3 Layers on top of Firmware Block protocol to produce a file abstraction\r
28a00297 4 of FV based files.\r
23c98c94 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
28a00297 14\r
15**/\r
16\r
17#include <DxeMain.h>\r
18\r
19#define KEYSIZE sizeof (UINTN)\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
0c2b5da8 28 FV2_DEVICE_SIGNATURE,\r
28a00297 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
0c2b5da8 38 KEYSIZE,\r
39 NULL,\r
40 FvGetVolumeInfo,\r
41 FvSetVolumeInfo\r
28a00297 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
162ed594 55/**\r
28a00297 56 given the supplied FW_VOL_BLOCK_PROTOCOL, allocate a buffer for output and\r
57 copy the volume header into it.\r
58\r
022c6d45 59 @param Fvb The FW_VOL_BLOCK_PROTOCOL instance from which to\r
60 read the volume header\r
61 @param FwVolHeader Pointer to pointer to allocated buffer in which\r
62 the volume header is returned.\r
28a00297 63\r
022c6d45 64 @retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated.\r
65 @retval EFI_SUCCESS Successfully read volume header to the allocated\r
162ed594 66 buffer.\r
28a00297 67\r
162ed594 68**/\r
69EFI_STATUS\r
70GetFwVolHeader (\r
71 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb,\r
72 OUT EFI_FIRMWARE_VOLUME_HEADER **FwVolHeader\r
73 )\r
28a00297 74{\r
75 EFI_STATUS Status;\r
76 EFI_FIRMWARE_VOLUME_HEADER TempFvh;\r
77 UINTN FvhLength;\r
78 UINT8 *Buffer;\r
79\r
80\r
81 //\r
82 //Determine the real length of FV header\r
83 //\r
84 FvhLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER);\r
85 Status = Fvb->Read (Fvb, 0, 0, &FvhLength, (UINT8 *)&TempFvh);\r
86\r
87 //\r
88 // Allocate a buffer for the caller\r
89 //\r
90 *FwVolHeader = CoreAllocateBootServicesPool (TempFvh.HeaderLength);\r
91 if (*FwVolHeader == NULL) {\r
92 return EFI_OUT_OF_RESOURCES;\r
93 }\r
94\r
95 //\r
96 // Copy the standard header into the buffer\r
97 //\r
98 CopyMem (*FwVolHeader, &TempFvh, sizeof (EFI_FIRMWARE_VOLUME_HEADER));\r
99\r
100 //\r
101 // Read the rest of the header\r
102 //\r
103 FvhLength = TempFvh.HeaderLength - sizeof (EFI_FIRMWARE_VOLUME_HEADER);\r
104 Buffer = (UINT8 *)*FwVolHeader + sizeof (EFI_FIRMWARE_VOLUME_HEADER);\r
105 Status = Fvb->Read (Fvb, 0, sizeof (EFI_FIRMWARE_VOLUME_HEADER), &FvhLength, Buffer);\r
106 if (EFI_ERROR (Status)) {\r
107 //\r
108 // Read failed so free buffer\r
109 //\r
110 CoreFreePool (*FwVolHeader);\r
111 }\r
022c6d45 112\r
28a00297 113 return Status;\r
114}\r
115\r
116\r
28a00297 117\r
162ed594 118/**\r
28a00297 119 Free FvDevice resource when error happens\r
120\r
022c6d45 121 @param FvDevice pointer to the FvDevice to be freed.\r
28a00297 122\r
162ed594 123**/\r
162ed594 124VOID\r
125FreeFvDeviceResource (\r
126 IN FV_DEVICE *FvDevice\r
127 )\r
28a00297 128{\r
129 FFS_FILE_LIST_ENTRY *FfsFileEntry;\r
130 LIST_ENTRY *NextEntry;\r
131\r
132 //\r
133 // Free File List Entry\r
134 //\r
135 FfsFileEntry = (FFS_FILE_LIST_ENTRY *)FvDevice->FfsFileListHeader.ForwardLink;\r
136 while (&FfsFileEntry->Link != &FvDevice->FfsFileListHeader) {\r
137 NextEntry = (&FfsFileEntry->Link)->ForwardLink;\r
022c6d45 138\r
28a00297 139 if (FfsFileEntry->StreamHandle != 0) {\r
140 //\r
141 // Close stream and free resources from SEP\r
142 //\r
797a9d67 143 CloseSectionStream (FfsFileEntry->StreamHandle);\r
28a00297 144 }\r
145\r
146 CoreFreePool (FfsFileEntry);\r
147\r
e94a9ff7 148 FfsFileEntry = (FFS_FILE_LIST_ENTRY *) NextEntry;\r
28a00297 149 }\r
150\r
151\r
152 //\r
153 // Free the cache\r
154 //\r
155 CoreFreePool (FvDevice->CachedFv);\r
156\r
157 //\r
158 // Free Volume Header\r
159 //\r
160 CoreFreePool (FvDevice->FwVolHeader);\r
161\r
162 return;\r
163}\r
164\r
165\r
28a00297 166\r
162ed594 167/**\r
e94a9ff7 168 Check if an FV is consistent and allocate cache for it.\r
28a00297 169\r
022c6d45 170 @param FvDevice A pointer to the FvDevice to be checked.\r
28a00297 171\r
022c6d45 172 @retval EFI_OUT_OF_RESOURCES No enough buffer could be allocated.\r
173 @retval EFI_SUCCESS FV is consistent and cache is allocated.\r
162ed594 174 @retval EFI_VOLUME_CORRUPTED File system is corrupted.\r
28a00297 175\r
162ed594 176**/\r
177EFI_STATUS\r
178FvCheck (\r
179 IN OUT FV_DEVICE *FvDevice\r
180 )\r
28a00297 181{\r
182 EFI_STATUS Status;\r
183 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
184 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
185 EFI_FVB_ATTRIBUTES FvbAttributes;\r
186 EFI_FV_BLOCK_MAP_ENTRY *BlockMap;\r
187 FFS_FILE_LIST_ENTRY *FfsFileEntry;\r
188 EFI_FFS_FILE_HEADER *FfsHeader;\r
189 UINT8 *CacheLocation;\r
190 UINTN LbaOffset;\r
191 UINTN Index;\r
192 EFI_LBA LbaIndex;\r
193 UINTN Size;\r
194 UINTN FileLength;\r
195 EFI_FFS_FILE_STATE FileState;\r
196 UINT8 *TopFvAddress;\r
197 UINTN TestLength;\r
198\r
199\r
200 Fvb = FvDevice->Fvb;\r
201 FwVolHeader = FvDevice->FwVolHeader;\r
022c6d45 202\r
28a00297 203 Status = Fvb->GetAttributes (Fvb, &FvbAttributes);\r
204 if (EFI_ERROR (Status)) {\r
205 return Status;\r
206 }\r
207\r
208 //\r
209 // Size is the size of the FV minus the head. We have already allocated\r
210 // the header to check to make sure the volume is valid\r
211 //\r
212 Size = (UINTN)(FwVolHeader->FvLength - FwVolHeader->HeaderLength);\r
213 FvDevice->CachedFv = CoreAllocateBootServicesPool (Size);\r
214\r
215 if (FvDevice->CachedFv == NULL) {\r
216 return EFI_OUT_OF_RESOURCES;\r
217 }\r
218\r
219 //\r
220 // Remember a pointer to the end fo the CachedFv\r
221 //\r
222 FvDevice->EndOfCachedFv = FvDevice->CachedFv + Size;\r
223\r
224 //\r
225 // Copy FV minus header into memory using the block map we have all ready\r
226 // read into memory.\r
227 //\r
228 BlockMap = FwVolHeader->BlockMap;\r
229 CacheLocation = FvDevice->CachedFv;\r
230 LbaIndex = 0;\r
231 LbaOffset = FwVolHeader->HeaderLength;\r
232 while ((BlockMap->NumBlocks != 0) || (BlockMap->Length != 0)) {\r
022c6d45 233\r
28a00297 234 for (Index = 0; Index < BlockMap->NumBlocks; Index ++) {\r
235\r
236 Size = BlockMap->Length;\r
237 if (Index == 0) {\r
238 //\r
239 // Cache does not include FV Header\r
240 //\r
241 Size -= LbaOffset;\r
242 }\r
243 Status = Fvb->Read (Fvb,\r
e94a9ff7 244 LbaIndex,\r
245 LbaOffset,\r
246 &Size,\r
247 CacheLocation\r
248 );\r
28a00297 249 //\r
250 // Not check EFI_BAD_BUFFER_SIZE, for Size = BlockMap->Length\r
251 //\r
252 if (EFI_ERROR (Status)) {\r
253 goto Done;\r
254 }\r
022c6d45 255\r
28a00297 256 //\r
257 // After we skip Fv Header always read from start of block\r
258 //\r
259 LbaOffset = 0;\r
260\r
261 LbaIndex++;\r
262 CacheLocation += Size;\r
263 }\r
264 BlockMap++;\r
265 }\r
266\r
267 //\r
268 // Scan to check the free space & File list\r
269 //\r
71f68914 270 if ((FvbAttributes & EFI_FVB2_ERASE_POLARITY) != 0) {\r
28a00297 271 FvDevice->ErasePolarity = 1;\r
272 } else {\r
273 FvDevice->ErasePolarity = 0;\r
022c6d45 274 }\r
28a00297 275\r
276\r
277 //\r
278 // go through the whole FV cache, check the consistence of the FV.\r
279 // Make a linked list off all the Ffs file headers\r
280 //\r
281 Status = EFI_SUCCESS;\r
282 InitializeListHead (&FvDevice->FfsFileListHeader);\r
283\r
284 //\r
285 // Build FFS list\r
286 //\r
e94a9ff7 287 FfsHeader = (EFI_FFS_FILE_HEADER *) FvDevice->CachedFv;\r
28a00297 288 TopFvAddress = FvDevice->EndOfCachedFv;\r
e94a9ff7 289 while ((UINT8 *) FfsHeader < TopFvAddress) {\r
28a00297 290\r
e94a9ff7 291 TestLength = TopFvAddress - ((UINT8 *) FfsHeader);\r
28a00297 292 if (TestLength > sizeof (EFI_FFS_FILE_HEADER)) {\r
293 TestLength = sizeof (EFI_FFS_FILE_HEADER);\r
294 }\r
295\r
296 if (IsBufferErased (FvDevice->ErasePolarity, FfsHeader, TestLength)) {\r
297 //\r
298 // We have found the free space so we are done!\r
299 //\r
300 goto Done;\r
301 }\r
302\r
303 if (!IsValidFfsHeader (FvDevice->ErasePolarity, FfsHeader, &FileState)) {\r
022c6d45 304 if ((FileState == EFI_FILE_HEADER_INVALID) ||\r
28a00297 305 (FileState == EFI_FILE_HEADER_CONSTRUCTION)) {\r
306 FfsHeader++;\r
28a00297 307 continue;\r
28a00297 308 } else {\r
309 //\r
310 // File system is corrputed\r
311 //\r
312 Status = EFI_VOLUME_CORRUPTED;\r
313 goto Done;\r
314 }\r
315 }\r
316\r
317 if (!IsValidFfsFile (FvDevice->ErasePolarity, FfsHeader)) {\r
318 //\r
319 // File system is corrupted\r
320 //\r
321 Status = EFI_VOLUME_CORRUPTED;\r
322 goto Done;\r
323 }\r
324\r
325 //\r
326 // Size[3] is a three byte array, read 4 bytes and throw one away\r
327 //\r
328 FileLength = *(UINT32 *)&FfsHeader->Size[0] & 0x00FFFFFF;\r
329\r
330 FileState = GetFileState (FvDevice->ErasePolarity, FfsHeader);\r
022c6d45 331\r
28a00297 332 //\r
333 // check for non-deleted file\r
334 //\r
335 if (FileState != EFI_FILE_DELETED) {\r
336 //\r
337 // Create a FFS list entry for each non-deleted file\r
338 //\r
339 FfsFileEntry = CoreAllocateZeroBootServicesPool (sizeof (FFS_FILE_LIST_ENTRY));\r
340 if (FfsFileEntry == NULL) {\r
341 Status = EFI_OUT_OF_RESOURCES;\r
342 goto Done;\r
343 }\r
022c6d45 344\r
28a00297 345 FfsFileEntry->FfsHeader = FfsHeader;\r
346 InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link);\r
347 }\r
348\r
349 FfsHeader = (EFI_FFS_FILE_HEADER *)(((UINT8 *)FfsHeader) + FileLength);\r
022c6d45 350\r
28a00297 351 //\r
352 // Adjust pointer to the next 8-byte aligned boundry.\r
353 //\r
354 FfsHeader = (EFI_FFS_FILE_HEADER *)(((UINTN)FfsHeader + 7) & ~0x07);\r
022c6d45 355\r
28a00297 356 }\r
357\r
358Done:\r
359 if (EFI_ERROR (Status)) {\r
360 FreeFvDeviceResource (FvDevice);\r
361 }\r
362\r
363 return Status;\r
364}\r
365\r
366\r
162ed594 367\r
368/**\r
369 This notification function is invoked when an instance of the\r
370 EFI_FW_VOLUME_BLOCK_PROTOCOL is produced. It layers an instance of the\r
371 EFI_FIRMWARE_VOLUME2_PROTOCOL on the same handle. This is the function where\r
372 the actual initialization of the EFI_FIRMWARE_VOLUME2_PROTOCOL is done.\r
373\r
022c6d45 374 @param Event The event that occured\r
162ed594 375 @param Context For EFI compatiblity. Not used.\r
376\r
377**/\r
28a00297 378VOID\r
379EFIAPI\r
380NotifyFwVolBlock (\r
381 IN EFI_EVENT Event,\r
382 IN VOID *Context\r
383 )\r
28a00297 384{\r
385 EFI_HANDLE Handle;\r
386 EFI_STATUS Status;\r
387 UINTN BufferSize;\r
388 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
0c2b5da8 389 EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;\r
28a00297 390 FV_DEVICE *FvDevice;\r
391 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
392 //\r
393 // Examine all new handles\r
394 //\r
395 for (;;) {\r
396 //\r
397 // Get the next handle\r
398 //\r
399 BufferSize = sizeof (Handle);\r
400 Status = CoreLocateHandle (\r
401 ByRegisterNotify,\r
402 NULL,\r
403 gEfiFwVolBlockNotifyReg,\r
404 &BufferSize,\r
405 &Handle\r
406 );\r
407\r
408 //\r
409 // If not found, we're done\r
410 //\r
411 if (EFI_NOT_FOUND == Status) {\r
412 break;\r
413 }\r
414\r
415 if (EFI_ERROR (Status)) {\r
416 continue;\r
417 }\r
022c6d45 418\r
28a00297 419 //\r
420 // Get the FirmwareVolumeBlock protocol on that handle\r
421 //\r
022c6d45 422 Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);\r
28a00297 423 ASSERT_EFI_ERROR (Status);\r
022c6d45 424\r
28a00297 425\r
426 //\r
427 // Make sure the Fv Header is O.K.\r
428 //\r
429 Status = GetFwVolHeader (Fvb, &FwVolHeader);\r
430 if (EFI_ERROR (Status)) {\r
431 return;\r
432 }\r
433\r
434 if (!VerifyFvHeaderChecksum (FwVolHeader)) {\r
435 CoreFreePool (FwVolHeader);\r
436 continue;\r
437 }\r
438\r
439\r
440 //\r
441 // Check to see that the file system is indeed formatted in a way we can\r
442 // understand it...\r
443 //\r
9767823f 444 if (!CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem2Guid)) {\r
28a00297 445 continue;\r
446 }\r
447\r
448 //\r
449 // Check if there is an FV protocol already installed in that handle\r
450 //\r
0c2b5da8 451 Status = CoreHandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv);\r
28a00297 452 if (!EFI_ERROR (Status)) {\r
453 //\r
454 // Update Fv to use a new Fvb\r
455 //\r
456 FvDevice = _CR (Fv, FV_DEVICE, Fv);\r
0c2b5da8 457 if (FvDevice->Signature == FV2_DEVICE_SIGNATURE) {\r
28a00297 458 //\r
459 // Only write into our device structure if it's our device structure\r
460 //\r
461 FvDevice->Fvb = Fvb;\r
462 }\r
463\r
464 } else {\r
465 //\r
466 // No FwVol protocol on the handle so create a new one\r
467 //\r
468 FvDevice = CoreAllocateCopyPool (sizeof (FV_DEVICE), &mFvDevice);\r
469 if (FvDevice == NULL) {\r
470 return;\r
471 }\r
022c6d45 472\r
e94a9ff7 473 FvDevice->Fvb = Fvb;\r
474 FvDevice->Handle = Handle;\r
475 FvDevice->FwVolHeader = FwVolHeader;\r
28a00297 476 FvDevice->Fv.ParentHandle = Fvb->ParentHandle;\r
022c6d45 477\r
28a00297 478 //\r
479 // Install an New FV protocol on the existing handle\r
480 //\r
481 Status = CoreInstallProtocolInterface (\r
482 &Handle,\r
0c2b5da8 483 &gEfiFirmwareVolume2ProtocolGuid,\r
28a00297 484 EFI_NATIVE_INTERFACE,\r
485 &FvDevice->Fv\r
486 );\r
487 ASSERT_EFI_ERROR (Status);\r
488 }\r
489 }\r
022c6d45 490\r
28a00297 491 return;\r
492}\r
493\r
494\r
162ed594 495\r
496/**\r
497 This routine is the driver initialization entry point. It initializes the\r
498 libraries, and registers two notification functions. These notification\r
499 functions are responsible for building the FV stack dynamically.\r
500\r
022c6d45 501 @param ImageHandle The image handle.\r
502 @param SystemTable The system table.\r
162ed594 503\r
504 @retval EFI_SUCCESS Function successfully returned.\r
505\r
506**/\r
28a00297 507EFI_STATUS\r
508EFIAPI\r
509FwVolDriverInit (\r
510 IN EFI_HANDLE ImageHandle,\r
511 IN EFI_SYSTEM_TABLE *SystemTable\r
512 )\r
28a00297 513{\r
514 gEfiFwVolBlockEvent = CoreCreateProtocolNotifyEvent (\r
515 &gEfiFirmwareVolumeBlockProtocolGuid,\r
516 TPL_CALLBACK,\r
517 NotifyFwVolBlock,\r
518 NULL,\r
519 &gEfiFwVolBlockNotifyReg,\r
520 TRUE\r
521 );\r
522 return EFI_SUCCESS;\r
523}\r
524\r
162ed594 525\r