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