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