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