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