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