1 /** @file
2   Implements write firmware file.
3 
4   Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions
8   of the BSD License which accompanies this distribution.  The
9   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 "FwVolDriver.h"
18 
19 /**
20   Calculate the checksum for the FFS header.
21 
22   @param FfsHeader   FFS File Header which needs to calculate the checksum
23 
24 **/
25 VOID
SetHeaderChecksum(IN EFI_FFS_FILE_HEADER * FfsHeader)26 SetHeaderChecksum (
27   IN EFI_FFS_FILE_HEADER *FfsHeader
28   )
29 {
30   EFI_FFS_FILE_STATE  State;
31   UINT8               FileChecksum;
32 
33   //
34   // The state and the File checksum are not included
35   //
36   State = FfsHeader->State;
37   FfsHeader->State = 0;
38 
39   FileChecksum = FfsHeader->IntegrityCheck.Checksum.File;
40   FfsHeader->IntegrityCheck.Checksum.File = 0;
41 
42   FfsHeader->IntegrityCheck.Checksum.Header = 0;
43 
44   if (IS_FFS_FILE2 (FfsHeader)) {
45     FfsHeader->IntegrityCheck.Checksum.Header = CalculateCheckSum8 (
46       (UINT8 *) FfsHeader,
47       sizeof (EFI_FFS_FILE_HEADER2)
48       );
49   } else {
50     FfsHeader->IntegrityCheck.Checksum.Header = CalculateCheckSum8 (
51       (UINT8 *) FfsHeader,
52       sizeof (EFI_FFS_FILE_HEADER)
53       );
54   }
55 
56   FfsHeader->State                          = State;
57   FfsHeader->IntegrityCheck.Checksum.File   = FileChecksum;
58 
59   return ;
60 }
61 
62 /**
63   Calculate the checksum for the FFS File.
64 
65   @param FfsHeader       FFS File Header which needs to calculate the checksum
66   @param ActualFileSize  The whole Ffs File Length.
67 
68 **/
69 VOID
SetFileChecksum(IN EFI_FFS_FILE_HEADER * FfsHeader,IN UINTN ActualFileSize)70 SetFileChecksum (
71   IN EFI_FFS_FILE_HEADER *FfsHeader,
72   IN UINTN               ActualFileSize
73   )
74 {
75   if ((FfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) != 0) {
76 
77     FfsHeader->IntegrityCheck.Checksum.File = 0;
78 
79     if (IS_FFS_FILE2 (FfsHeader)) {
80       FfsHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 (
81         (UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2),
82         ActualFileSize - sizeof (EFI_FFS_FILE_HEADER2)
83         );
84     } else {
85       FfsHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 (
86         (UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER),
87         ActualFileSize - sizeof (EFI_FFS_FILE_HEADER)
88         );
89     }
90 
91   } else {
92 
93     FfsHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;
94 
95   }
96 
97   return ;
98 }
99 
100 /**
101   Get the alignment value from File Attributes.
102 
103   @param FfsAttributes  FFS attribute
104 
105   @return Alignment value.
106 
107 **/
108 UINTN
GetRequiredAlignment(IN EFI_FV_FILE_ATTRIBUTES FfsAttributes)109 GetRequiredAlignment (
110   IN EFI_FV_FILE_ATTRIBUTES FfsAttributes
111   )
112 {
113   UINTN AlignmentValue;
114 
115   AlignmentValue = FfsAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT;
116 
117   if (AlignmentValue <= 3) {
118     return 0x08;
119   }
120 
121   if (AlignmentValue > 16) {
122     //
123     // Anyway, we won't reach this code
124     //
125     return 0x08;
126   }
127 
128   return (UINTN)1 << AlignmentValue;
129 
130 }
131 
132 /**
133   Calculate the leading Pad file size to meet the alignment requirement.
134 
135   @param FvDevice          Cached Firmware Volume.
136   @param StartAddress      The starting address to write the FFS File.
137   @param BufferSize        The FFS File Buffer Size.
138   @param RequiredAlignment FFS File Data alignment requirement.
139 
140   @return The required Pad File Size.
141 
142 **/
143 UINTN
CalculatePadFileSize(IN FV_DEVICE * FvDevice,IN EFI_PHYSICAL_ADDRESS StartAddress,IN UINTN BufferSize,IN UINTN RequiredAlignment)144 CalculatePadFileSize (
145   IN FV_DEVICE            *FvDevice,
146   IN EFI_PHYSICAL_ADDRESS StartAddress,
147   IN UINTN                BufferSize,
148   IN UINTN                RequiredAlignment
149   )
150 {
151   UINTN DataStartPos;
152   UINTN RelativePos;
153   UINTN PadSize;
154 
155   if (BufferSize > 0x00FFFFFF) {
156     DataStartPos  = (UINTN) StartAddress + sizeof (EFI_FFS_FILE_HEADER2);
157   } else {
158     DataStartPos  = (UINTN) StartAddress + sizeof (EFI_FFS_FILE_HEADER);
159   }
160   RelativePos   = DataStartPos - (UINTN) FvDevice->CachedFv;
161 
162   PadSize       = 0;
163 
164   while ((RelativePos & (RequiredAlignment - 1)) != 0) {
165     RelativePos++;
166     PadSize++;
167   }
168   //
169   // If padsize is 0, no pad file needed;
170   // If padsize is great than 24, then pad file can be created
171   //
172   if ((PadSize == 0) || (PadSize >= sizeof (EFI_FFS_FILE_HEADER))) {
173     return PadSize;
174   }
175 
176   //
177   // Perhaps following method can save space
178   //
179   RelativePos = DataStartPos - (UINTN) FvDevice->CachedFv + sizeof (EFI_FFS_FILE_HEADER);
180   PadSize     = sizeof (EFI_FFS_FILE_HEADER);
181 
182   while ((RelativePos & (RequiredAlignment - 1)) != 0) {
183     RelativePos++;
184     PadSize++;
185   }
186 
187   return PadSize;
188 }
189 
190 /**
191   Convert EFI_FV_FILE_ATTRIBUTES to FFS_FILE_ATTRIBUTES.
192 
193   @param FvFileAttrib    The value of EFI_FV_FILE_ATTRIBUTES
194   @param FfsFileAttrib   Pointer to the got FFS_FILE_ATTRIBUTES value.
195 
196 **/
197 VOID
FvFileAttrib2FfsFileAttrib(IN EFI_FV_FILE_ATTRIBUTES FvFileAttrib,OUT UINT8 * FfsFileAttrib)198 FvFileAttrib2FfsFileAttrib (
199   IN     EFI_FV_FILE_ATTRIBUTES  FvFileAttrib,
200   OUT UINT8                      *FfsFileAttrib
201   )
202 {
203   UINT8 FvFileAlignment;
204   UINT8 FfsFileAlignment;
205 
206   FvFileAlignment   = (UINT8) (FvFileAttrib & EFI_FV_FILE_ATTRIB_ALIGNMENT);
207   FfsFileAlignment  = 0;
208 
209   switch (FvFileAlignment) {
210   case 0:
211     //
212     // fall through
213     //
214   case 1:
215     //
216     // fall through
217     //
218   case 2:
219     //
220     // fall through
221     //
222   case 3:
223     //
224     // fall through
225     //
226     FfsFileAlignment = 0;
227     break;
228 
229   case 4:
230     //
231     // fall through
232     //
233   case 5:
234     //
235     // fall through
236     //
237   case 6:
238     //
239     // fall through
240     //
241     FfsFileAlignment = 1;
242     break;
243 
244   case 7:
245     //
246     // fall through
247     //
248   case 8:
249     //
250     // fall through
251     //
252     FfsFileAlignment = 2;
253     break;
254 
255   case 9:
256     FfsFileAlignment = 3;
257     break;
258 
259   case 10:
260     //
261     // fall through
262     //
263   case 11:
264     //
265     // fall through
266     //
267     FfsFileAlignment = 4;
268     break;
269 
270   case 12:
271     //
272     // fall through
273     //
274   case 13:
275     //
276     // fall through
277     //
278   case 14:
279     //
280     // fall through
281     //
282     FfsFileAlignment = 5;
283     break;
284 
285   case 15:
286     FfsFileAlignment = 6;
287     break;
288 
289   case 16:
290     FfsFileAlignment = 7;
291     break;
292   }
293 
294   *FfsFileAttrib = (UINT8) (FfsFileAlignment << 3);
295 
296   return ;
297 }
298 
299 /**
300   Locate a free space entry that can hold this FFS file.
301 
302   @param FvDevice          Cached Firmware Volume.
303   @param Size              The FFS file size.
304   @param RequiredAlignment FFS File Data alignment requirement.
305   @param PadSize           Pointer to the size of leading Pad File.
306   @param FreeSpaceEntry    Pointer to the Free Space Entry that meets the requirement.
307 
308   @retval EFI_SUCCESS     The free space entry is found.
309   @retval EFI_NOT_FOUND   The free space entry can't be found.
310 
311 **/
312 EFI_STATUS
FvLocateFreeSpaceEntry(IN FV_DEVICE * FvDevice,IN UINTN Size,IN UINTN RequiredAlignment,OUT UINTN * PadSize,OUT FREE_SPACE_ENTRY ** FreeSpaceEntry)313 FvLocateFreeSpaceEntry (
314   IN  FV_DEVICE             *FvDevice,
315   IN  UINTN                 Size,
316   IN  UINTN                 RequiredAlignment,
317   OUT UINTN                 *PadSize,
318   OUT FREE_SPACE_ENTRY      **FreeSpaceEntry
319   )
320 {
321   FREE_SPACE_ENTRY  *FreeSpaceListEntry;
322   LIST_ENTRY        *Link;
323   UINTN             PadFileSize;
324 
325   Link                = FvDevice->FreeSpaceHeader.ForwardLink;
326   FreeSpaceListEntry  = (FREE_SPACE_ENTRY *) Link;
327 
328   //
329   // Loop the free space entry list to find one that can hold the
330   // required the file size
331   //
332   while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) {
333     PadFileSize = CalculatePadFileSize (
334                     FvDevice,
335                     (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceListEntry->StartingAddress,
336                     Size,
337                     RequiredAlignment
338                     );
339     if (FreeSpaceListEntry->Length >= Size + PadFileSize) {
340       *FreeSpaceEntry = FreeSpaceListEntry;
341       *PadSize        = PadFileSize;
342       return EFI_SUCCESS;
343     }
344 
345     FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink;
346   }
347 
348   return EFI_NOT_FOUND;
349 
350 }
351 
352 /**
353   Locate Pad File for writing, this is got from FV Cache.
354 
355   @param FvDevice           Cached Firmware Volume.
356   @param Size               The required FFS file size.
357   @param RequiredAlignment  FFS File Data alignment requirement.
358   @param PadSize            Pointer to the size of leading Pad File.
359   @param PadFileEntry       Pointer to the Pad File Entry that meets the requirement.
360 
361   @retval EFI_SUCCESS     The required pad file is found.
362   @retval EFI_NOT_FOUND   The required pad file can't be found.
363 
364 **/
365 EFI_STATUS
FvLocatePadFile(IN FV_DEVICE * FvDevice,IN UINTN Size,IN UINTN RequiredAlignment,OUT UINTN * PadSize,OUT FFS_FILE_LIST_ENTRY ** PadFileEntry)366 FvLocatePadFile (
367   IN  FV_DEVICE           *FvDevice,
368   IN  UINTN               Size,
369   IN  UINTN               RequiredAlignment,
370   OUT UINTN               *PadSize,
371   OUT FFS_FILE_LIST_ENTRY **PadFileEntry
372   )
373 {
374   FFS_FILE_LIST_ENTRY *FileEntry;
375   EFI_FFS_FILE_STATE  FileState;
376   EFI_FFS_FILE_HEADER *FileHeader;
377   UINTN               PadAreaLength;
378   UINTN               PadFileSize;
379   UINTN               HeaderSize;
380 
381   FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink;
382 
383   //
384   // travel through the whole file list to get the pad file entry
385   //
386   while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) {
387 
388     FileHeader  = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader;
389     FileState   = GetFileState (FvDevice->ErasePolarity, FileHeader);
390 
391     if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) {
392       //
393       // we find one valid pad file, check its free area length
394       //
395       if (IS_FFS_FILE2 (FileHeader)) {
396         HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
397         PadAreaLength = FFS_FILE2_SIZE (FileHeader) - HeaderSize;
398       } else {
399         HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
400         PadAreaLength = FFS_FILE_SIZE (FileHeader) - HeaderSize;
401       }
402 
403       PadFileSize = CalculatePadFileSize (
404                       FvDevice,
405                       (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + HeaderSize,
406                       Size,
407                       RequiredAlignment
408                       );
409       if (PadAreaLength >= (Size + PadFileSize)) {
410         *PadSize      = PadFileSize;
411         *PadFileEntry = FileEntry;
412         return EFI_SUCCESS;
413       }
414     }
415 
416     FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink);
417   }
418 
419   return EFI_NOT_FOUND;
420 }
421 
422 /**
423   Locate a suitable pad file for multiple file writing.
424 
425   @param FvDevice          Cached Firmware Volume.
426   @param NumOfFiles        The number of Files that needed updating
427   @param BufferSize        The array of each file size.
428   @param RequiredAlignment The array of of FFS File Data alignment requirement.
429   @param PadSize           The array of size of each leading Pad File.
430   @param TotalSizeNeeded   The totalsize that can hold these files.
431   @param PadFileEntry      Pointer to the Pad File Entry that meets the requirement.
432 
433   @retval EFI_SUCCESS     The required pad file is found.
434   @retval EFI_NOT_FOUND   The required pad file can't be found.
435 
436 **/
437 EFI_STATUS
FvSearchSuitablePadFile(IN FV_DEVICE * FvDevice,IN UINTN NumOfFiles,IN UINTN * BufferSize,IN UINTN * RequiredAlignment,OUT UINTN * PadSize,OUT UINTN * TotalSizeNeeded,OUT FFS_FILE_LIST_ENTRY ** PadFileEntry)438 FvSearchSuitablePadFile (
439   IN FV_DEVICE              *FvDevice,
440   IN UINTN                  NumOfFiles,
441   IN UINTN                  *BufferSize,
442   IN UINTN                  *RequiredAlignment,
443   OUT UINTN                 *PadSize,
444   OUT UINTN                 *TotalSizeNeeded,
445   OUT FFS_FILE_LIST_ENTRY   **PadFileEntry
446   )
447 {
448   FFS_FILE_LIST_ENTRY *FileEntry;
449   EFI_FFS_FILE_STATE  FileState;
450   EFI_FFS_FILE_HEADER *FileHeader;
451   UINTN               PadAreaLength;
452   UINTN               TotalSize;
453   UINTN               Index;
454   UINTN               HeaderSize;
455 
456   FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink;
457 
458   //
459   // travel through the whole file list to get the pad file entry
460   //
461   while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) {
462 
463     FileHeader  = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader;
464     FileState   = GetFileState (FvDevice->ErasePolarity, FileHeader);
465 
466     if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) {
467       //
468       // we find one valid pad file, check its length
469       //
470       if (IS_FFS_FILE2 (FileHeader)) {
471         HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
472         PadAreaLength = FFS_FILE2_SIZE (FileHeader) - HeaderSize;
473       } else {
474         HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
475         PadAreaLength = FFS_FILE_SIZE (FileHeader) - HeaderSize;
476       }
477       TotalSize     = 0;
478 
479       for (Index = 0; Index < NumOfFiles; Index++) {
480         PadSize[Index] = CalculatePadFileSize (
481                       FvDevice,
482                       (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + HeaderSize + TotalSize,
483                       BufferSize[Index],
484                       RequiredAlignment[Index]
485                       );
486         TotalSize += PadSize[Index];
487         TotalSize += BufferSize[Index];
488 
489         if (TotalSize > PadAreaLength) {
490           break;
491         }
492       }
493 
494       if (PadAreaLength >= TotalSize) {
495         *PadFileEntry     = FileEntry;
496         *TotalSizeNeeded  = TotalSize;
497         return EFI_SUCCESS;
498       }
499     }
500 
501     FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink);
502   }
503 
504   return EFI_NOT_FOUND;
505 }
506 
507 /**
508   Locate a Free Space entry which can hold these files, including
509   meeting the alignment requirements.
510 
511   @param FvDevice          Cached Firmware Volume.
512   @param NumOfFiles        The number of Files that needed updating
513   @param BufferSize        The array of each file size.
514   @param RequiredAlignment The array of of FFS File Data alignment requirement.
515   @param PadSize           The array of size of each leading Pad File.
516   @param TotalSizeNeeded   The got total size that can hold these files.
517   @param FreeSpaceEntry    The Free Space Entry that can hold these files.
518 
519   @retval EFI_SUCCESS     The free space entry is found.
520   @retval EFI_NOT_FOUND   The free space entry can't be found.
521 
522 **/
523 EFI_STATUS
FvSearchSuitableFreeSpace(IN FV_DEVICE * FvDevice,IN UINTN NumOfFiles,IN UINTN * BufferSize,IN UINTN * RequiredAlignment,OUT UINTN * PadSize,OUT UINTN * TotalSizeNeeded,OUT FREE_SPACE_ENTRY ** FreeSpaceEntry)524 FvSearchSuitableFreeSpace (
525   IN FV_DEVICE              *FvDevice,
526   IN UINTN                  NumOfFiles,
527   IN UINTN                  *BufferSize,
528   IN UINTN                  *RequiredAlignment,
529   OUT UINTN                 *PadSize,
530   OUT UINTN                 *TotalSizeNeeded,
531   OUT FREE_SPACE_ENTRY      **FreeSpaceEntry
532   )
533 {
534   FREE_SPACE_ENTRY  *FreeSpaceListEntry;
535   LIST_ENTRY        *Link;
536   UINTN             TotalSize;
537   UINTN             Index;
538   UINT8             *StartAddr;
539 
540   Link                = FvDevice->FreeSpaceHeader.ForwardLink;
541 
542   FreeSpaceListEntry  = (FREE_SPACE_ENTRY *) Link;
543 
544   while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) {
545     TotalSize = 0;
546     StartAddr = FreeSpaceListEntry->StartingAddress;
547 
548     //
549     // Calculate the totalsize we need
550     //
551     for (Index = 0; Index < NumOfFiles; Index++) {
552       //
553       // Perhaps we don't need an EFI_FFS_FILE_HEADER, the first file
554       // have had its leading pad file.
555       //
556       PadSize[Index] = CalculatePadFileSize (
557                     FvDevice,
558                     (EFI_PHYSICAL_ADDRESS) (UINTN) StartAddr + TotalSize,
559                     BufferSize[Index],
560                     RequiredAlignment[Index]
561                     );
562 
563       TotalSize += PadSize[Index];
564       TotalSize += BufferSize[Index];
565 
566       if (TotalSize > FreeSpaceListEntry->Length) {
567         break;
568       }
569     }
570 
571     if (FreeSpaceListEntry->Length >= TotalSize) {
572       *FreeSpaceEntry   = FreeSpaceListEntry;
573       *TotalSizeNeeded  = TotalSize;
574       return EFI_SUCCESS;
575     }
576 
577     FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink;
578   }
579 
580   return EFI_NOT_FOUND;
581 }
582 
583 /**
584   Calculate the length of the remaining space in FV.
585 
586   @param FvDevice        Cached Firmware Volume
587   @param Offset          Current offset to FV base address.
588   @param Lba             LBA number for the current offset.
589   @param LOffset         Offset in block for the current offset.
590 
591   @return the length of remaining space.
592 
593 **/
594 UINTN
CalculateRemainingLength(IN FV_DEVICE * FvDevice,IN UINTN Offset,OUT EFI_LBA * Lba,OUT UINTN * LOffset)595 CalculateRemainingLength (
596   IN     FV_DEVICE                            *FvDevice,
597   IN     UINTN                                Offset,
598   OUT  EFI_LBA                                *Lba,
599   OUT  UINTN                                  *LOffset
600   )
601 {
602   LIST_ENTRY      *Link;
603   LBA_ENTRY       *LbaEntry;
604   UINTN           Count;
605 
606   Count     = 0;
607   *Lba      = 0;
608   Link      = FvDevice->LbaHeader.ForwardLink;
609   LbaEntry  = (LBA_ENTRY *) Link;
610 
611   while (&LbaEntry->Link != &FvDevice->LbaHeader) {
612     if (Count > Offset) {
613       break;
614     }
615 
616     Count += LbaEntry->BlockLength;
617     (*Lba)++;
618     Link      = LbaEntry->Link.ForwardLink;
619     LbaEntry  = (LBA_ENTRY *) Link;
620   }
621 
622   if (Count <= Offset) {
623     return 0;
624   }
625 
626   Link      = LbaEntry->Link.BackLink;
627   LbaEntry  = (LBA_ENTRY *) Link;
628 
629   (*Lba)--;
630   *LOffset  = (UINTN) (LbaEntry->BlockLength - (Count - Offset));
631 
632   Count     = 0;
633   while (&LbaEntry->Link != &FvDevice->LbaHeader) {
634 
635     Count += LbaEntry->BlockLength;
636 
637     Link      = LbaEntry->Link.ForwardLink;
638     LbaEntry  = (LBA_ENTRY *) Link;
639   }
640 
641   Count -= *LOffset;
642 
643   return Count;
644 }
645 
646 /**
647   Writes data beginning at Lba:Offset from FV. The write terminates either
648   when *NumBytes of data have been written, or when the firmware end is
649   reached.  *NumBytes is updated to reflect the actual number of bytes
650   written.
651 
652   @param FvDevice        Cached Firmware Volume
653   @param Offset          Offset in the block at which to begin write
654   @param NumBytes        At input, indicates the requested write size.
655                          At output, indicates the actual number of bytes written.
656   @param Buffer          Buffer containing source data for the write.
657 
658   @retval EFI_SUCCESS  Data is successfully written into FV.
659   @return error        Data is failed written.
660 
661 **/
662 EFI_STATUS
FvcWrite(IN FV_DEVICE * FvDevice,IN UINTN Offset,IN OUT UINTN * NumBytes,IN UINT8 * Buffer)663 FvcWrite (
664   IN     FV_DEVICE                            *FvDevice,
665   IN     UINTN                                Offset,
666   IN OUT UINTN                                *NumBytes,
667   IN     UINT8                                *Buffer
668   )
669 {
670   EFI_STATUS                          Status;
671   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
672   EFI_LBA                             Lba;
673   UINTN                               LOffset;
674   EFI_FVB_ATTRIBUTES_2                FvbAttributes;
675   UINTN                               RemainingLength;
676   UINTN                               WriteLength;
677   UINT8                               *TmpBuffer;
678 
679   LOffset = 0;
680   RemainingLength = CalculateRemainingLength (FvDevice, Offset, &Lba, &LOffset);
681   if ((UINTN) (*NumBytes) > RemainingLength) {
682     *NumBytes = (UINTN) RemainingLength;
683     return EFI_INVALID_PARAMETER;
684   }
685 
686   Fvb = FvDevice->Fvb;
687 
688   Status = Fvb->GetAttributes (
689                   Fvb,
690                   &FvbAttributes
691                   );
692   if (EFI_ERROR (Status)) {
693     return Status;
694   }
695 
696   if ((FvbAttributes & EFI_FV2_WRITE_STATUS) == 0) {
697     return EFI_ACCESS_DENIED;
698   }
699 
700   RemainingLength = *NumBytes;
701   WriteLength     = RemainingLength;
702   TmpBuffer       = Buffer;
703 
704   do {
705     Status = Fvb->Write (
706                     Fvb,
707                     Lba,
708                     LOffset,
709                     &WriteLength,
710                     TmpBuffer
711                     );
712     if (!EFI_ERROR (Status)) {
713       goto Done;
714     }
715 
716     if (Status == EFI_BAD_BUFFER_SIZE) {
717       Lba++;
718       LOffset = 0;
719       TmpBuffer += WriteLength;
720       RemainingLength -= WriteLength;
721       WriteLength = (UINTN) RemainingLength;
722 
723       continue;
724     } else {
725       return Status;
726     }
727   } while (1);
728 
729 Done:
730   return EFI_SUCCESS;
731 }
732 
733 /**
734   Create a new FFS file into Firmware Volume device.
735 
736   @param FvDevice        Cached Firmware Volume.
737   @param FfsFileBuffer   A buffer that holds an FFS file,(it contains
738                          a File Header which is in init state).
739   @param BufferSize      The size of FfsFileBuffer.
740   @param ActualFileSize  The actual file length, it may not be multiples of 8.
741   @param FileName        The FFS File Name.
742   @param FileType        The FFS File Type.
743   @param FileAttributes  The Attributes of the FFS File to be created.
744 
745   @retval EFI_SUCCESS           FFS fle is added into FV.
746   @retval EFI_INVALID_PARAMETER File type is not valid.
747   @retval EFI_DEVICE_ERROR      FV doesn't set writable attribute.
748   @retval EFI_NOT_FOUND         FV has no enough space for the added file.
749 
750 **/
751 EFI_STATUS
FvCreateNewFile(IN FV_DEVICE * FvDevice,IN UINT8 * FfsFileBuffer,IN UINTN BufferSize,IN UINTN ActualFileSize,IN EFI_GUID * FileName,IN EFI_FV_FILETYPE FileType,IN EFI_FV_FILE_ATTRIBUTES FileAttributes)752 FvCreateNewFile (
753   IN FV_DEVICE                *FvDevice,
754   IN UINT8                    *FfsFileBuffer,
755   IN UINTN                    BufferSize,
756   IN UINTN                    ActualFileSize,
757   IN EFI_GUID                 *FileName,
758   IN EFI_FV_FILETYPE          FileType,
759   IN EFI_FV_FILE_ATTRIBUTES   FileAttributes
760   )
761 {
762   EFI_STATUS                          Status;
763   EFI_FFS_FILE_HEADER                 *FileHeader;
764   EFI_PHYSICAL_ADDRESS                BufferPtr;
765   UINTN                               Offset;
766   UINTN                               NumBytesWritten;
767   UINTN                               StateOffset;
768   FREE_SPACE_ENTRY                    *FreeSpaceEntry;
769   UINTN                               RequiredAlignment;
770   UINTN                               PadFileSize;
771   FFS_FILE_LIST_ENTRY                 *PadFileEntry;
772   EFI_FFS_FILE_ATTRIBUTES             TmpFileAttribute;
773   FFS_FILE_LIST_ENTRY                 *FfsFileEntry;
774   UINTN                               HeaderSize;
775 
776   //
777   // File Type: 0x0E~0xE0 are reserved
778   //
779   if ((FileType > EFI_FV_FILETYPE_SMM_CORE) && (FileType < 0xE0)) {
780     return EFI_INVALID_PARAMETER;
781   }
782 
783   //
784   // First find a free space that can hold this image.
785   // Check alignment, FFS at least must be aligned at 8-byte boundry
786   //
787   RequiredAlignment = GetRequiredAlignment (FileAttributes);
788 
789   Status = FvLocateFreeSpaceEntry (
790             FvDevice,
791             BufferSize,
792             RequiredAlignment,
793             &PadFileSize,
794             &FreeSpaceEntry
795             );
796   if (EFI_ERROR (Status)) {
797     //
798     // Maybe we need to find a PAD file that can hold this image
799     //
800     Status = FvCreateNewFileInsidePadFile (
801               FvDevice,
802               FfsFileBuffer,
803               BufferSize,
804               ActualFileSize,
805               FileName,
806               FileType,
807               FileAttributes
808               );
809 
810     return Status;
811   }
812 
813   BufferPtr     = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress;
814 
815   //
816   // If we need a leading PAD File, create it first.
817   //
818   if (PadFileSize != 0) {
819     Status = FvCreatePadFileInFreeSpace (
820               FvDevice,
821               FreeSpaceEntry,
822               PadFileSize - sizeof (EFI_FFS_FILE_HEADER),
823               &PadFileEntry
824               );
825     if (EFI_ERROR (Status)) {
826       return Status;
827     }
828   }
829   //
830   // Maybe we create a pad file, so re-get the free space starting address
831   // and length
832   //
833   BufferPtr     = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress;
834 
835   //
836   // File creation step 1: Allocate File Header,
837   // Mark EFI_FILE_HEADER_CONSTRUCTION bit to TRUE,
838   // Write Name, IntegrityCheck.Header, Type, Attributes, and Size
839   //
840   FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileBuffer;
841   if (ActualFileSize > 0x00FFFFFF) {
842     HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
843   } else {
844     HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
845   }
846   SetFileState (EFI_FILE_HEADER_CONSTRUCTION, FileHeader);
847 
848   Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
849   StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;
850 
851   NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
852   Status = FvcWrite (
853             FvDevice,
854             StateOffset,
855             &NumBytesWritten,
856             &FileHeader->State
857             );
858   if (EFI_ERROR (Status)) {
859     return Status;
860   }
861   //
862   // update header 2 cache
863   //
864   CopyMem (
865     (UINT8 *) (UINTN) BufferPtr,
866     FileHeader,
867     HeaderSize
868     );
869 
870   //
871   // update Free Space Entry, now need to substract the file header length
872   //
873   FreeSpaceEntry->StartingAddress += HeaderSize;
874   FreeSpaceEntry->Length -= HeaderSize;
875 
876   CopyGuid (&FileHeader->Name, FileName);
877   FileHeader->Type = FileType;
878 
879   //
880   // Convert FvFileAttribute to FfsFileAttributes
881   //
882   FvFileAttrib2FfsFileAttrib (FileAttributes, &TmpFileAttribute);
883 
884   FileHeader->Attributes = TmpFileAttribute;
885 
886   //
887   // File size is including the FFS File Header.
888   //
889   if (ActualFileSize > 0x00FFFFFF) {
890     ((EFI_FFS_FILE_HEADER2 *) FileHeader)->ExtendedSize = (UINT32) ActualFileSize;
891     *(UINT32 *) FileHeader->Size &= 0xFF000000;
892     FileHeader->Attributes |= FFS_ATTRIB_LARGE_FILE;
893   } else {
894     *(UINT32 *) FileHeader->Size &= 0xFF000000;
895     *(UINT32 *) FileHeader->Size |= ActualFileSize;
896   }
897 
898   SetHeaderChecksum (FileHeader);
899 
900   Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
901 
902   NumBytesWritten = HeaderSize;
903   Status = FvcWrite (
904             FvDevice,
905             Offset,
906             &NumBytesWritten,
907             (UINT8 *) FileHeader
908             );
909   if (EFI_ERROR (Status)) {
910     return Status;
911   }
912   //
913   // update header 2 cache
914   //
915   CopyMem (
916     (UINT8 *) (UINTN) BufferPtr,
917     FileHeader,
918     HeaderSize
919     );
920 
921   //
922   // end of step 1
923   //
924   // File creation step 2:
925   // MARK EFI_FILE_HEADER_VALID bit to TRUE,
926   // Write IntegrityCheck.File, File Data
927   //
928   SetFileState (EFI_FILE_HEADER_VALID, FileHeader);
929 
930   Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
931   StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;
932 
933   NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
934   Status = FvcWrite (
935             FvDevice,
936             StateOffset,
937             &NumBytesWritten,
938             &FileHeader->State
939             );
940   if (EFI_ERROR (Status)) {
941     return Status;
942   }
943   //
944   // update header 2 cache
945   //
946   CopyMem (
947     (UINT8 *) (UINTN) BufferPtr,
948     FileHeader,
949     HeaderSize
950     );
951 
952   //
953   // update Free Space Entry, now need to substract the file data length
954   //
955   FreeSpaceEntry->StartingAddress += (BufferSize - HeaderSize);
956   FreeSpaceEntry->Length -= (BufferSize - HeaderSize);
957 
958   //
959   // Calculate File Checksum
960   //
961   SetFileChecksum (FileHeader, ActualFileSize);
962 
963   Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
964 
965   NumBytesWritten = BufferSize;
966   Status = FvcWrite (
967             FvDevice,
968             Offset,
969             &NumBytesWritten,
970             FfsFileBuffer
971             );
972   if (EFI_ERROR (Status)) {
973     return Status;
974   }
975   //
976   // each time write block successfully, write also to cache
977   //
978   CopyMem (
979     (UINT8 *) (UINTN) BufferPtr,
980     FfsFileBuffer,
981     NumBytesWritten
982     );
983 
984   //
985   // Step 3: Mark EFI_FILE_DATA_VALID to TRUE
986   //
987   SetFileState (EFI_FILE_DATA_VALID, FileHeader);
988 
989   Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
990   StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;
991 
992   NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
993   Status = FvcWrite (
994             FvDevice,
995             StateOffset,
996             &NumBytesWritten,
997             &FileHeader->State
998             );
999   if (EFI_ERROR (Status)) {
1000     return Status;
1001   }
1002   //
1003   // update header 2 cache
1004   //
1005   CopyMem (
1006     (UINT8 *) (UINTN) BufferPtr,
1007     FileHeader,
1008     HeaderSize
1009     );
1010 
1011   //
1012   // If successfully, insert an FfsFileEntry at the end of ffs file list
1013   //
1014 
1015   FfsFileEntry            = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY));
1016   ASSERT (FfsFileEntry   != NULL);
1017   FfsFileEntry->FfsHeader = (UINT8 *) (UINTN) BufferPtr;
1018   InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link);
1019 
1020   //
1021   // Set cache file to this file
1022   //
1023   FvDevice->CurrentFfsFile = FfsFileEntry;
1024 
1025   return EFI_SUCCESS;
1026 }
1027 
1028 /**
1029   Update a File, so after successful update, there are 2 files existing
1030   in FV, one is marked for deleted, and another one is valid.
1031 
1032   @param FvDevice          Cached Firmware Volume.
1033   @param FfsFileBuffer     A buffer that holds an FFS file,(it contains
1034                            a File Header which is in init state).
1035   @param BufferSize        The size of FfsFileBuffer.
1036   @param ActualFileSize    The actual file length, it may not be multiples of 8.
1037   @param FileName          The FFS File Name.
1038   @param NewFileType       The FFS File Type.
1039   @param NewFileAttributes The Attributes of the FFS File to be created.
1040 
1041   @retval EFI_SUCCESS           FFS fle is updated into FV.
1042   @retval EFI_INVALID_PARAMETER File type is not valid.
1043   @retval EFI_DEVICE_ERROR      FV doesn't set writable attribute.
1044   @retval EFI_NOT_FOUND         FV has no enough space for the added file.
1045                                 FFS with same file name is not found in FV.
1046 
1047 **/
1048 EFI_STATUS
FvUpdateFile(IN FV_DEVICE * FvDevice,IN UINT8 * FfsFileBuffer,IN UINTN BufferSize,IN UINTN ActualFileSize,IN EFI_GUID * FileName,IN EFI_FV_FILETYPE NewFileType,IN EFI_FV_FILE_ATTRIBUTES NewFileAttributes)1049 FvUpdateFile (
1050   IN FV_DEVICE                *FvDevice,
1051   IN UINT8                    *FfsFileBuffer,
1052   IN UINTN                    BufferSize,
1053   IN UINTN                    ActualFileSize,
1054   IN EFI_GUID                 *FileName,
1055   IN EFI_FV_FILETYPE          NewFileType,
1056   IN EFI_FV_FILE_ATTRIBUTES   NewFileAttributes
1057   )
1058 {
1059   EFI_STATUS                          Status;
1060   EFI_FIRMWARE_VOLUME2_PROTOCOL       *Fv;
1061   UINTN                               NumBytesWritten;
1062   EFI_FV_FILETYPE                     OldFileType;
1063   EFI_FV_FILE_ATTRIBUTES              OldFileAttributes;
1064   UINTN                               OldFileSize;
1065   EFI_FFS_FILE_HEADER                 *OldFileHeader;
1066   UINTN                               OldOffset;
1067   UINTN                               OldStateOffset;
1068   FFS_FILE_LIST_ENTRY                 *OldFfsFileEntry;
1069   UINTN                               Key;
1070   EFI_GUID                            FileNameGuid;
1071 
1072   Fv  = &FvDevice->Fv;
1073 
1074   //
1075   // Step 1, find old file,
1076   // Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE in the older header
1077   //
1078 
1079   //
1080   // Check if the file was read last time.
1081   //
1082   OldFileHeader   = NULL;
1083   OldFfsFileEntry = FvDevice->CurrentFfsFile;
1084 
1085   if (OldFfsFileEntry != NULL) {
1086     OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader;
1087   }
1088 
1089   if ((OldFfsFileEntry == NULL) || (!CompareGuid (&OldFileHeader->Name, FileName))) {
1090     Key = 0;
1091     do {
1092       OldFileType = 0;
1093       Status = Fv->GetNextFile (
1094                     Fv,
1095                     &Key,
1096                     &OldFileType,
1097                     &FileNameGuid,
1098                     &OldFileAttributes,
1099                     &OldFileSize
1100                     );
1101       if (EFI_ERROR (Status)) {
1102         return Status;
1103       }
1104     } while (!CompareGuid (&FileNameGuid, FileName));
1105 
1106     //
1107     // Get FfsFileEntry from the search key
1108     //
1109     OldFfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key;
1110 
1111     //
1112     // Double check file state before being ready to be removed
1113     //
1114     OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader;
1115   } else {
1116     //
1117     // Mark the cache file to invalid
1118     //
1119     FvDevice->CurrentFfsFile = NULL;
1120   }
1121   //
1122   // Update File: Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE
1123   //
1124   SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader);
1125 
1126   OldOffset       = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv);
1127   OldStateOffset  = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader;
1128 
1129   NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
1130   Status = FvcWrite (
1131             FvDevice,
1132             OldStateOffset,
1133             &NumBytesWritten,
1134             &OldFileHeader->State
1135             );
1136   if (EFI_ERROR (Status)) {
1137     //
1138     // if failed, write the bit back in the cache, its XOR operation.
1139     //
1140     SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader);
1141 
1142     return Status;
1143   }
1144 
1145   //
1146   // Step 2, Create New Files
1147   //
1148   Status = FvCreateNewFile (
1149             FvDevice,
1150             FfsFileBuffer,
1151             BufferSize,
1152             ActualFileSize,
1153             FileName,
1154             NewFileType,
1155             NewFileAttributes
1156             );
1157   if (EFI_ERROR (Status)) {
1158     return Status;
1159   }
1160 
1161   //
1162   // If successfully, remove this file entry,
1163   // although delete file may fail.
1164   //
1165   (OldFfsFileEntry->Link.BackLink)->ForwardLink = OldFfsFileEntry->Link.ForwardLink;
1166   (OldFfsFileEntry->Link.ForwardLink)->BackLink = OldFfsFileEntry->Link.BackLink;
1167   FreePool (OldFfsFileEntry);
1168 
1169   //
1170   // Step 3: Delete old files,
1171   // by marking EFI_FILE_DELETED to TRUE
1172   //
1173   SetFileState (EFI_FILE_DELETED, OldFileHeader);
1174 
1175   OldOffset       = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv);
1176   OldStateOffset  = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader;
1177 
1178   NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
1179   Status = FvcWrite (
1180             FvDevice,
1181             OldStateOffset,
1182             &NumBytesWritten,
1183             &OldFileHeader->State
1184             );
1185   if (EFI_ERROR (Status)) {
1186     //
1187     // if failed, write the bit back in the cache, its XOR operation.
1188     //
1189     SetFileState (EFI_FILE_DELETED, OldFileHeader);
1190 
1191     return Status;
1192   }
1193 
1194   return EFI_SUCCESS;
1195 }
1196 
1197 /**
1198   Deleted a given file from FV device.
1199 
1200   @param FvDevice        Cached Firmware Volume.
1201   @param NameGuid        The FFS File Name.
1202 
1203   @retval EFI_SUCCESS    FFS file with the specified FFS name is removed.
1204   @retval EFI_NOT_FOUND  FFS file with the specified FFS name is not found.
1205 
1206 **/
1207 EFI_STATUS
FvDeleteFile(IN FV_DEVICE * FvDevice,IN EFI_GUID * NameGuid)1208 FvDeleteFile (
1209   IN FV_DEVICE  *FvDevice,
1210   IN EFI_GUID   *NameGuid
1211   )
1212 {
1213   EFI_STATUS                          Status;
1214   UINTN                               Key;
1215   EFI_GUID                            FileNameGuid;
1216   EFI_FV_FILETYPE                     FileType;
1217   EFI_FV_FILE_ATTRIBUTES              FileAttributes;
1218   UINTN                               FileSize;
1219   EFI_FFS_FILE_HEADER                 *FileHeader;
1220   FFS_FILE_LIST_ENTRY                 *FfsFileEntry;
1221   EFI_FFS_FILE_STATE                  FileState;
1222   EFI_FIRMWARE_VOLUME2_PROTOCOL        *Fv;
1223   UINTN                               Offset;
1224   UINTN                               StateOffset;
1225   UINTN                               NumBytesWritten;
1226 
1227   Fv  = &FvDevice->Fv;
1228 
1229   //
1230   // Check if the file was read last time.
1231   //
1232   FileHeader    = NULL;
1233   FfsFileEntry  = FvDevice->CurrentFfsFile;
1234 
1235   if (FfsFileEntry != NULL) {
1236     FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader;
1237   }
1238 
1239   if ((FfsFileEntry == NULL) || (!CompareGuid (&FileHeader->Name, NameGuid))) {
1240     //
1241     // Next search for the file using GetNextFile
1242     //
1243     Key = 0;
1244     do {
1245       FileType = 0;
1246       Status = Fv->GetNextFile (
1247                     Fv,
1248                     &Key,
1249                     &FileType,
1250                     &FileNameGuid,
1251                     &FileAttributes,
1252                     &FileSize
1253                     );
1254       if (EFI_ERROR (Status)) {
1255         return Status;
1256       }
1257     } while (!CompareGuid (&FileNameGuid, NameGuid));
1258 
1259     //
1260     // Get FfsFileEntry from the search key
1261     //
1262     FfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key;
1263 
1264     //
1265     // Double check file state before being ready to be removed
1266     //
1267     FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader;
1268   } else {
1269     //
1270     // Mark the cache file to NULL
1271     //
1272     FvDevice->CurrentFfsFile = NULL;
1273   }
1274 
1275   FileState = GetFileState (FvDevice->ErasePolarity, FileHeader);
1276 
1277   if (FileState == EFI_FILE_HEADER_INVALID) {
1278     return EFI_NOT_FOUND;
1279   }
1280 
1281   if (FileState == EFI_FILE_DELETED) {
1282     return EFI_NOT_FOUND;
1283   }
1284   //
1285   // Delete File: Mark EFI_FILE_DELETED to TRUE
1286   //
1287   SetFileState (EFI_FILE_DELETED, FileHeader);
1288 
1289   Offset          = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader - FvDevice->CachedFv);
1290   StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;
1291 
1292   NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
1293   Status = FvcWrite (
1294             FvDevice,
1295             StateOffset,
1296             &NumBytesWritten,
1297             &FileHeader->State
1298             );
1299   if (EFI_ERROR (Status)) {
1300     //
1301     // if failed, write the bit back in the cache, its XOR operation.
1302     //
1303     SetFileState (EFI_FILE_DELETED, FileHeader);
1304 
1305     return Status;
1306   }
1307   //
1308   // If successfully, remove this file entry
1309   //
1310   FvDevice->CurrentFfsFile                    = NULL;
1311 
1312   (FfsFileEntry->Link.BackLink)->ForwardLink  = FfsFileEntry->Link.ForwardLink;
1313   (FfsFileEntry->Link.ForwardLink)->BackLink  = FfsFileEntry->Link.BackLink;
1314   FreePool (FfsFileEntry);
1315 
1316   return EFI_SUCCESS;
1317 }
1318 
1319 /**
1320   Writes one or more files to the firmware volume.
1321 
1322   @param  This                   Indicates the calling context.
1323   @param  NumberOfFiles          Number of files.
1324   @param  WritePolicy            WritePolicy indicates the level of reliability
1325                                  for the write in the event of a power failure or
1326                                  other system failure during the write operation.
1327   @param  FileData               FileData is an pointer to an array of
1328                                  EFI_FV_WRITE_DATA. Each element of array
1329                                  FileData represents a file to be written.
1330 
1331   @retval EFI_SUCCESS            Files successfully written to firmware volume
1332   @retval EFI_OUT_OF_RESOURCES   Not enough buffer to be allocated.
1333   @retval EFI_DEVICE_ERROR       Device error.
1334   @retval EFI_WRITE_PROTECTED    Write protected.
1335   @retval EFI_NOT_FOUND          Not found.
1336   @retval EFI_INVALID_PARAMETER  Invalid parameter.
1337   @retval EFI_UNSUPPORTED        This function not supported.
1338 
1339 **/
1340 EFI_STATUS
1341 EFIAPI
FvWriteFile(IN CONST EFI_FIRMWARE_VOLUME2_PROTOCOL * This,IN UINT32 NumberOfFiles,IN EFI_FV_WRITE_POLICY WritePolicy,IN EFI_FV_WRITE_FILE_DATA * FileData)1342 FvWriteFile (
1343   IN CONST EFI_FIRMWARE_VOLUME2_PROTOCOL   *This,
1344   IN UINT32                         NumberOfFiles,
1345   IN EFI_FV_WRITE_POLICY            WritePolicy,
1346   IN EFI_FV_WRITE_FILE_DATA         *FileData
1347   )
1348 {
1349   EFI_STATUS                          Status;
1350   UINTN                               Index1;
1351   UINTN                               Index2;
1352   UINT8                               *FileBuffer;
1353   UINTN                               BufferSize;
1354   UINTN                               ActualSize;
1355   UINT8                               ErasePolarity;
1356   FV_DEVICE                           *FvDevice;
1357   EFI_FV_FILETYPE                     FileType;
1358   EFI_FV_FILE_ATTRIBUTES              FileAttributes;
1359   UINTN                               Size;
1360   BOOLEAN                             CreateNewFile[MAX_FILES];
1361   UINTN                               NumDelete;
1362   EFI_FV_ATTRIBUTES                   FvAttributes;
1363   UINT32                              AuthenticationStatus;
1364   UINTN                               HeaderSize;
1365 
1366   if (NumberOfFiles > MAX_FILES) {
1367     return EFI_UNSUPPORTED;
1368   }
1369 
1370   Status = EFI_SUCCESS;
1371 
1372   SetMem (CreateNewFile, NumberOfFiles, TRUE);
1373 
1374   FvDevice  = FV_DEVICE_FROM_THIS (This);
1375 
1376   //
1377   // First check the volume attributes.
1378   //
1379   Status = This->GetVolumeAttributes (
1380                   This,
1381                   &FvAttributes
1382                   );
1383   if (EFI_ERROR (Status)) {
1384     return Status;
1385   }
1386   //
1387   // Can we have write right?
1388   //
1389   if ((FvAttributes & EFI_FV2_WRITE_STATUS) == 0) {
1390     return EFI_WRITE_PROTECTED;
1391   }
1392 
1393   ErasePolarity = FvDevice->ErasePolarity;
1394 
1395   //
1396   // Loop for all files
1397   //
1398   NumDelete = 0;
1399   for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
1400 
1401     if ((FileData[Index1].BufferSize + sizeof (EFI_FFS_FILE_HEADER) > 0x00FFFFFF) && !FvDevice->IsFfs3Fv) {
1402       //
1403       // Found a file needs a FFS3 formatted file to store it, but it is in a non-FFS3 formatted FV.
1404       //
1405       DEBUG ((EFI_D_ERROR, "FFS3 formatted file can't be written in a non-FFS3 formatted FV.\n"));
1406       return EFI_INVALID_PARAMETER;
1407     }
1408 
1409     if (FileData[Index1].BufferSize == 0) {
1410       //
1411       // Here we will delete this file
1412       //
1413       Status = This->ReadFile (
1414                       This,
1415                       FileData[Index1].NameGuid,
1416                       NULL,
1417                       &Size,
1418                       &FileType,
1419                       &FileAttributes,
1420                       &AuthenticationStatus
1421                       );
1422       if (!EFI_ERROR (Status)) {
1423         NumDelete++;
1424       } else {
1425         return Status;
1426       }
1427     }
1428 
1429     if (FileData[Index1].Type == EFI_FV_FILETYPE_FFS_PAD) {
1430       //
1431       // According to PI spec, on EFI_FV_FILETYPE_FFS_PAD:
1432       // "Standard firmware file system services will not return the handle of any pad files,
1433       // nor will they permit explicit creation of such files."
1434       //
1435       return EFI_INVALID_PARAMETER;
1436     }
1437   }
1438 
1439   if ((NumDelete != NumberOfFiles) && (NumDelete != 0)) {
1440     //
1441     // A delete was request with a multiple file write
1442     //
1443     return EFI_INVALID_PARAMETER;
1444   }
1445 
1446   if (NumDelete == NumberOfFiles) {
1447     for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
1448       //
1449       // Delete Files
1450       //
1451       Status = FvDeleteFile (FvDevice, FileData[Index1].NameGuid);
1452       if (EFI_ERROR (Status)) {
1453         return Status;
1454       }
1455     }
1456 
1457     return EFI_SUCCESS;
1458   }
1459 
1460   for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
1461     Status = This->ReadFile (
1462                     This,
1463                     FileData[Index1].NameGuid,
1464                     NULL,
1465                     &Size,
1466                     &FileType,
1467                     &FileAttributes,
1468                     &AuthenticationStatus
1469                     );
1470     if (!EFI_ERROR (Status)) {
1471       CreateNewFile[Index1] = FALSE;
1472     } else if (Status == EFI_NOT_FOUND) {
1473       CreateNewFile[Index1] = TRUE;
1474     } else {
1475       return Status;
1476     }
1477     //
1478     // Checking alignment
1479     //
1480     if ((FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT) != 0) {
1481       UINT8 FFSAlignmentValue;
1482       UINT8 FvAlignmentValue;
1483 
1484       FFSAlignmentValue = (UINT8) (FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT);
1485       FvAlignmentValue = (UINT8) (((UINT32) (FvAttributes & EFI_FV2_ALIGNMENT)) >> 16);
1486 
1487       if (FFSAlignmentValue > FvAlignmentValue) {
1488         return EFI_INVALID_PARAMETER;
1489       }
1490     }
1491   }
1492 
1493   if ((WritePolicy != EFI_FV_RELIABLE_WRITE) && (WritePolicy != EFI_FV_UNRELIABLE_WRITE)) {
1494     return EFI_INVALID_PARAMETER;
1495   }
1496   //
1497   // Checking the reliable write is supported by FV
1498   //
1499 
1500   if ((WritePolicy == EFI_FV_RELIABLE_WRITE) && (NumberOfFiles > 1)) {
1501     //
1502     // Only for multiple files, reliable write is meaningful
1503     //
1504     Status = FvCreateMultipleFiles (
1505               FvDevice,
1506               NumberOfFiles,
1507               FileData,
1508               CreateNewFile
1509               );
1510 
1511     return Status;
1512   }
1513 
1514   for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
1515     //
1516     // Making Buffersize QWORD boundry, and add file tail.
1517     //
1518     HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
1519     ActualSize = FileData[Index1].BufferSize + HeaderSize;
1520     if (ActualSize > 0x00FFFFFF) {
1521       HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
1522       ActualSize = FileData[Index1].BufferSize + HeaderSize;
1523     }
1524     BufferSize  = ActualSize;
1525 
1526     while ((BufferSize & 0x07) != 0) {
1527       BufferSize++;
1528     }
1529 
1530     FileBuffer = AllocateZeroPool (BufferSize);
1531     if (FileBuffer == NULL) {
1532       return Status;
1533     }
1534     //
1535     // Copy File Data into FileBuffer
1536     //
1537     CopyMem (
1538       FileBuffer + HeaderSize,
1539       FileData[Index1].Buffer,
1540       FileData[Index1].BufferSize
1541       );
1542 
1543     if (ErasePolarity == 1) {
1544       //
1545       // Fill the file header and padding byte with Erase Byte
1546       //
1547       for (Index2 = 0; Index2 < HeaderSize; Index2++) {
1548         FileBuffer[Index2] = (UINT8)~FileBuffer[Index2];
1549       }
1550 
1551       for (Index2 = ActualSize; Index2 < BufferSize; Index2++) {
1552         FileBuffer[Index2] = (UINT8)~FileBuffer[Index2];
1553       }
1554     }
1555 
1556     if (CreateNewFile[Index1]) {
1557       Status = FvCreateNewFile (
1558                 FvDevice,
1559                 FileBuffer,
1560                 BufferSize,
1561                 ActualSize,
1562                 FileData[Index1].NameGuid,
1563                 FileData[Index1].Type,
1564                 FileData[Index1].FileAttributes
1565                 );
1566     } else {
1567       Status = FvUpdateFile (
1568                 FvDevice,
1569                 FileBuffer,
1570                 BufferSize,
1571                 ActualSize,
1572                 FileData[Index1].NameGuid,
1573                 FileData[Index1].Type,
1574                 FileData[Index1].FileAttributes
1575                 );
1576     }
1577 
1578     FreePool (FileBuffer);
1579 
1580     if (EFI_ERROR (Status)) {
1581       return Status;
1582     }
1583   }
1584 
1585   return EFI_SUCCESS;
1586 }
1587