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