2 The implementation supports Capusle on Disk.
4 Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include "CapsuleOnDisk.h"
12 Return if this capsule is a capsule name capsule, based upon CapsuleHeader.
14 @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER
16 @retval TRUE It is a capsule name capsule.
17 @retval FALSE It is not a capsule name capsule.
20 IsCapsuleNameCapsule (
21 IN EFI_CAPSULE_HEADER
*CapsuleHeader
25 Check the integrity of the capsule name capsule.
26 If the capsule is vaild, return the physical address of each capsule name string.
28 @param[in] CapsuleHeader Pointer to the capsule header of a capsule name capsule.
29 @param[out] CapsuleNameNum Number of capsule name.
31 @retval NULL Capsule name capsule is not valid.
32 @retval CapsuleNameBuf Array of capsule name physical address.
35 EFI_PHYSICAL_ADDRESS
*
36 ValidateCapsuleNameCapsuleIntegrity (
37 IN EFI_CAPSULE_HEADER
*CapsuleHeader
,
38 OUT UINTN
*CapsuleNameNum
41 UINT8
*CapsuleNamePtr
;
42 UINT8
*CapsuleNameBufStart
;
43 UINT8
*CapsuleNameBufEnd
;
46 EFI_PHYSICAL_ADDRESS
*CapsuleNameBuf
;
48 if (!IsCapsuleNameCapsule (CapsuleHeader
)) {
53 // Total string size must be even.
55 if (((CapsuleHeader
->CapsuleImageSize
- CapsuleHeader
->HeaderSize
) & BIT0
) != 0) {
61 CapsuleNameBufStart
= (UINT8
*) CapsuleHeader
+ CapsuleHeader
->HeaderSize
;
64 // If strings are not aligned on a 16-bit boundary, reallocate memory for it.
66 if (((UINTN
) CapsuleNameBufStart
& BIT0
) != 0) {
67 CapsuleNameBufStart
= AllocateCopyPool (CapsuleHeader
->CapsuleImageSize
- CapsuleHeader
->HeaderSize
, CapsuleNameBufStart
);
70 CapsuleNameBufEnd
= CapsuleNameBufStart
+ CapsuleHeader
->CapsuleImageSize
- CapsuleHeader
->HeaderSize
;
72 CapsuleNamePtr
= CapsuleNameBufStart
;
73 while (CapsuleNamePtr
< CapsuleNameBufEnd
) {
74 StringSize
= StrnSizeS ((CHAR16
*) CapsuleNamePtr
, (CapsuleNameBufEnd
- CapsuleNamePtr
)/sizeof(CHAR16
));
75 CapsuleNamePtr
+= StringSize
;
82 if (CapsuleNamePtr
!= CapsuleNameBufEnd
) {
83 if (CapsuleNameBufStart
!= (UINT8
*)CapsuleHeader
+ CapsuleHeader
->HeaderSize
) {
84 FreePool (CapsuleNameBufStart
);
89 CapsuleNameBuf
= AllocatePool (*CapsuleNameNum
* sizeof (EFI_PHYSICAL_ADDRESS
));
90 if (CapsuleNameBuf
== NULL
) {
91 if (CapsuleNameBufStart
!= (UINT8
*)CapsuleHeader
+ CapsuleHeader
->HeaderSize
) {
92 FreePool (CapsuleNameBufStart
);
97 CapsuleNamePtr
= CapsuleNameBufStart
;
98 while (CapsuleNamePtr
< CapsuleNameBufEnd
) {
99 StringSize
= StrnSizeS ((CHAR16
*) CapsuleNamePtr
, (CapsuleNameBufEnd
- CapsuleNamePtr
)/sizeof(CHAR16
));
100 CapsuleNameBuf
[Index
] = (EFI_PHYSICAL_ADDRESS
)(UINTN
) CapsuleNamePtr
;
101 CapsuleNamePtr
+= StringSize
;
105 return CapsuleNameBuf
;
109 This routine is called to upper case given unicode string.
111 @param[in] Str String to upper case
113 @retval upper cased string after process
124 for (Cptr
= Str
; *Cptr
; Cptr
++) {
125 if (L
'a' <= *Cptr
&& *Cptr
<= L
'z') {
126 *Cptr
= *Cptr
- L
'a' + L
'A';
134 This routine is used to return substring before period '.' or '\0'
135 Caller should respsonsible of substr space allocation & free
137 @param[in] Str String to check
138 @param[out] SubStr First part of string before period or '\0'
139 @param[out] SubStrLen Length of first part of string
144 GetSubStringBeforePeriod (
151 for (Index
= 0; Str
[Index
] != L
'.' && Str
[Index
] != L
'\0'; Index
++) {
152 SubStr
[Index
] = Str
[Index
];
155 SubStr
[Index
] = L
'\0';
160 This routine pad the string in tail with input character.
162 @param[in] StrBuf Str buffer to be padded, should be enough room for
163 @param[in] PadLen Expected padding length
164 @param[in] Character Character used to pad
177 for (Index
= 0; StrBuf
[Index
] != L
'\0'; Index
++);
180 StrBuf
[Index
] = Character
;
185 StrBuf
[Index
] = L
'\0';
189 This routine find the offset of the last period '.' of string. If No period exists
190 function FileNameExtension is set to L'\0'
192 @param[in] FileName File name to split between last period
193 @param[out] FileNameFirst First FileName before last period
194 @param[out] FileNameExtension FileName after last period
199 SplitFileNameExtension (
201 OUT CHAR16
*FileNameFirst
,
202 OUT CHAR16
*FileNameExtension
208 StringLen
= StrnLenS(FileName
, MAX_FILE_NAME_SIZE
);
209 for (Index
= StringLen
; Index
> 0 && FileName
[Index
] != L
'.'; Index
--);
212 // No period exists. No FileName Extension
214 if (Index
== 0 && FileName
[Index
] != L
'.') {
215 FileNameExtension
[0] = L
'\0';
218 StrCpyS(FileNameExtension
, MAX_FILE_NAME_SIZE
, &FileName
[Index
+1]);
222 // Copy First file name
224 StrnCpyS(FileNameFirst
, MAX_FILE_NAME_SIZE
, FileName
, Index
);
225 FileNameFirst
[Index
] = L
'\0';
229 This routine is called to get all boot options in the order determnined by:
233 @param[out] OptionBuf BootList buffer to all boot options returned
234 @param[out] OptionCount BootList count of all boot options returned
236 @retval EFI_SUCCESS There is no error when processing capsule
240 GetBootOptionInOrder(
241 OUT EFI_BOOT_MANAGER_LOAD_OPTION
**OptionBuf
,
242 OUT UINTN
*OptionCount
248 CHAR16 BootOptionName
[20];
249 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOrderOptionBuf
;
250 UINTN BootOrderCount
;
251 EFI_BOOT_MANAGER_LOAD_OPTION BootNextOptionEntry
;
253 EFI_BOOT_MANAGER_LOAD_OPTION
*TempBuf
;
255 BootOrderOptionBuf
= NULL
;
263 // First Get BootOption from "BootNext"
265 DataSize
= sizeof(BootNext
);
266 Status
= gRT
->GetVariable (
267 EFI_BOOT_NEXT_VARIABLE_NAME
,
268 &gEfiGlobalVariableGuid
,
274 // BootNext variable is a single UINT16
276 if (!EFI_ERROR(Status
) && DataSize
== sizeof(UINT16
)) {
278 // Add the boot next boot option
280 UnicodeSPrint (BootOptionName
, sizeof (BootOptionName
), L
"Boot%04x", BootNext
);
281 ZeroMem(&BootNextOptionEntry
, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION
));
282 Status
= EfiBootManagerVariableToLoadOption (BootOptionName
, &BootNextOptionEntry
);
284 if (!EFI_ERROR(Status
)) {
290 // Second get BootOption from "BootOrder"
292 BootOrderOptionBuf
= EfiBootManagerGetLoadOptions (&BootOrderCount
, LoadOptionTypeBoot
);
293 if (BootNextCount
== 0 && BootOrderCount
== 0) {
294 return EFI_NOT_FOUND
;
298 // At least one BootOption is found
300 TempBuf
= AllocatePool(sizeof(EFI_BOOT_MANAGER_LOAD_OPTION
) * (BootNextCount
+ BootOrderCount
));
301 if (TempBuf
!= NULL
) {
302 if (BootNextCount
== 1) {
303 CopyMem(TempBuf
, &BootNextOptionEntry
, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION
));
306 if (BootOrderCount
> 0) {
307 CopyMem(TempBuf
+ BootNextCount
, BootOrderOptionBuf
, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION
) * BootOrderCount
);
310 *OptionBuf
= TempBuf
;
311 *OptionCount
= BootNextCount
+ BootOrderCount
;
312 Status
= EFI_SUCCESS
;
314 Status
= EFI_OUT_OF_RESOURCES
;
317 FreePool(BootOrderOptionBuf
);
323 This routine is called to get boot option by OptionNumber.
325 @param[in] Number The OptionNumber of boot option
326 @param[out] OptionBuf BootList buffer to all boot options returned
328 @retval EFI_SUCCESS There is no error when getting boot option
332 GetBootOptionByNumber(
334 OUT EFI_BOOT_MANAGER_LOAD_OPTION
**OptionBuf
338 CHAR16 BootOptionName
[20];
339 EFI_BOOT_MANAGER_LOAD_OPTION BootOption
;
341 UnicodeSPrint (BootOptionName
, sizeof (BootOptionName
), L
"Boot%04x", Number
);
342 ZeroMem (&BootOption
, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION
));
343 Status
= EfiBootManagerVariableToLoadOption (BootOptionName
, &BootOption
);
345 if (!EFI_ERROR (Status
)) {
346 *OptionBuf
= AllocatePool (sizeof (EFI_BOOT_MANAGER_LOAD_OPTION
));
347 if (*OptionBuf
!= NULL
) {
348 CopyMem (*OptionBuf
, &BootOption
, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION
));
349 Status
= EFI_SUCCESS
;
351 Status
= EFI_OUT_OF_RESOURCES
;
359 Get Active EFI System Partition within GPT based on device path.
361 @param[in] DevicePath Device path to find a active EFI System Partition
362 @param[out] FsHandle BootList points to all boot options returned
364 @retval EFI_SUCCESS Active EFI System Partition is succesfully found
365 @retval EFI_NOT_FOUND No Active EFI System Partition is found
369 GetEfiSysPartitionFromDevPath(
370 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
371 OUT EFI_HANDLE
*FsHandle
375 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePath
;
376 HARDDRIVE_DEVICE_PATH
*Hd
;
378 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
381 // Check if the device path contains GPT node
383 TempDevicePath
= DevicePath
;
384 while (!IsDevicePathEnd (TempDevicePath
)) {
385 if ((DevicePathType (TempDevicePath
) == MEDIA_DEVICE_PATH
) &&
386 (DevicePathSubType (TempDevicePath
) == MEDIA_HARDDRIVE_DP
)) {
387 Hd
= (HARDDRIVE_DEVICE_PATH
*)TempDevicePath
;
388 if (Hd
->MBRType
== MBR_TYPE_EFI_PARTITION_TABLE_HEADER
) {
392 TempDevicePath
= NextDevicePathNode (TempDevicePath
);
395 if (!IsDevicePathEnd (TempDevicePath
)) {
397 // Search for EFI system partition protocol on full device path in Boot Option
399 Status
= gBS
->LocateDevicePath (&gEfiPartTypeSystemPartGuid
, &DevicePath
, &Handle
);
402 // Search for simple file system on this handler
404 if (!EFI_ERROR(Status
)) {
405 Status
= gBS
->HandleProtocol(Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
406 if (!EFI_ERROR(Status
)) {
413 return EFI_NOT_FOUND
;
417 This routine is called to get Simple File System protocol on the first EFI system partition found in
418 active boot option. The boot option list is detemined in order by
422 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
423 device like USB can get enumerated.
424 @param[in, out] LoadOptionNumber On input, specify the boot option to get EFI system partition.
425 On output, return the OptionNumber of the boot option where EFI
426 system partition is got from.
427 @param[out] FsFsHandle Simple File System Protocol found on first active EFI system partition
429 @retval EFI_SUCCESS Simple File System protocol found for EFI system partition
430 @retval EFI_NOT_FOUND No Simple File System protocol found for EFI system partition
434 GetEfiSysPartitionFromActiveBootOption(
436 IN OUT UINT16
**LoadOptionNumber
,
437 OUT EFI_HANDLE
*FsHandle
441 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptionBuf
;
444 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
445 EFI_DEVICE_PATH_PROTOCOL
*CurFullPath
;
446 EFI_DEVICE_PATH_PROTOCOL
*PreFullPath
;
451 if (*LoadOptionNumber
!= NULL
) {
453 Status
= GetBootOptionByNumber(**LoadOptionNumber
, &BootOptionBuf
);
454 if (EFI_ERROR(Status
)) {
455 DEBUG ((DEBUG_ERROR
, "GetBootOptionByIndex Failed %x! No BootOption available for connection\n", Status
));
459 Status
= GetBootOptionInOrder(&BootOptionBuf
, &BootOptionNum
);
460 if (EFI_ERROR(Status
)) {
461 DEBUG ((DEBUG_ERROR
, "GetBootOptionInOrder Failed %x! No BootOption available for connection\n", Status
));
467 // Search BootOptionList to check if it is an active boot option with EFI system partition
468 // 1. Connect device path
469 // 2. expend short/plug in devicepath
472 for (Index
= 0; Index
< BootOptionNum
; Index
++) {
474 // Get the boot option from the link list
476 DevicePath
= BootOptionBuf
[Index
].FilePath
;
479 // Skip inactive or legacy boot options
481 if ((BootOptionBuf
[Index
].Attributes
& LOAD_OPTION_ACTIVE
) == 0 ||
482 DevicePathType (DevicePath
) == BBS_DEVICE_PATH
) {
487 CHAR16
*DevicePathStr
;
489 DevicePathStr
= ConvertDevicePathToText(DevicePath
, TRUE
, TRUE
);
490 if (DevicePathStr
!= NULL
){
491 DEBUG((DEBUG_INFO
, "Try BootOption %s\n", DevicePathStr
));
492 FreePool(DevicePathStr
);
494 DEBUG((DEBUG_INFO
, "DevicePathToStr failed\n"));
500 // Try every full device Path generated from bootoption
503 PreFullPath
= CurFullPath
;
504 CurFullPath
= EfiBootManagerGetNextLoadOptionDevicePath(DevicePath
, CurFullPath
);
506 if (PreFullPath
!= NULL
) {
507 FreePool (PreFullPath
);
510 if (CurFullPath
== NULL
) {
512 // No Active EFI system partition is found in BootOption device path
514 Status
= EFI_NOT_FOUND
;
519 CHAR16
*DevicePathStr1
;
521 DevicePathStr1
= ConvertDevicePathToText(CurFullPath
, TRUE
, TRUE
);
522 if (DevicePathStr1
!= NULL
){
523 DEBUG((DEBUG_INFO
, "Full device path %s\n", DevicePathStr1
));
524 FreePool(DevicePathStr1
);
529 // Make sure the boot option device path connected.
530 // Only handle first device in boot option. Other optional device paths are described as OSV specific
531 // FullDevice could contain extra directory & file info. So don't check connection status here.
533 EfiBootManagerConnectDevicePath (CurFullPath
, NULL
);
534 Status
= GetEfiSysPartitionFromDevPath(CurFullPath
, FsHandle
);
537 // Some relocation device like USB need more time to get enumerated
539 while (EFI_ERROR(Status
) && MaxRetry
> 0) {
540 EfiBootManagerConnectDevicePath(CurFullPath
, NULL
);
543 // Search for EFI system partition protocol on full device path in Boot Option
545 Status
= GetEfiSysPartitionFromDevPath(CurFullPath
, FsHandle
);
546 if (!EFI_ERROR(Status
)) {
549 DEBUG((DEBUG_ERROR
, "GetEfiSysPartitionFromDevPath Loop %x\n", Status
));
551 // Stall 100ms if connection failed to ensure USB stack is ready
556 } while(EFI_ERROR(Status
));
559 // Find a qualified Simple File System
561 if (!EFI_ERROR(Status
)) {
568 // Return the OptionNumber of the boot option where EFI system partition is got from
570 if (*LoadOptionNumber
== NULL
) {
571 *LoadOptionNumber
= AllocateCopyPool (sizeof(UINT16
), (UINT16
*) &BootOptionBuf
[Index
].OptionNumber
);
572 if (*LoadOptionNumber
== NULL
) {
573 Status
= EFI_OUT_OF_RESOURCES
;
578 // No qualified EFI system partition found
580 if (*FsHandle
== NULL
) {
581 Status
= EFI_NOT_FOUND
;
585 CHAR16
*DevicePathStr2
;
586 if (*FsHandle
!= NULL
) {
587 DevicePathStr2
= ConvertDevicePathToText(CurFullPath
, TRUE
, TRUE
);
588 if (DevicePathStr2
!= NULL
){
589 DEBUG((DEBUG_INFO
, "Found Active EFI System Partion on %s\n", DevicePathStr2
));
590 FreePool(DevicePathStr2
);
593 DEBUG((DEBUG_INFO
, "Failed to found Active EFI System Partion\n"));
597 if (CurFullPath
!= NULL
) {
598 FreePool(CurFullPath
);
602 // Free BootOption Buffer
604 for (Index
= 0; Index
< BootOptionNum
; Index
++) {
605 if (BootOptionBuf
[Index
].Description
!= NULL
) {
606 FreePool(BootOptionBuf
[Index
].Description
);
609 if (BootOptionBuf
[Index
].FilePath
!= NULL
) {
610 FreePool(BootOptionBuf
[Index
].FilePath
);
613 if (BootOptionBuf
[Index
].OptionalData
!= NULL
) {
614 FreePool(BootOptionBuf
[Index
].OptionalData
);
618 FreePool(BootOptionBuf
);
625 This routine is called to get all file infos with in a given dir & with given file attribute, the file info is listed in
626 alphabetical order described in UEFI spec.
628 @param[in] Dir Directory file handler
629 @param[in] FileAttr Attribute of file to be red from directory
630 @param[out] FileInfoList File images info list red from directory
631 @param[out] FileNum File images number red from directory
633 @retval EFI_SUCCESS File FileInfo list in the given
637 GetFileInfoListInAlphabetFromDir(
638 IN EFI_FILE_HANDLE Dir
,
640 OUT LIST_ENTRY
*FileInfoList
,
645 FILE_INFO_ENTRY
*NewFileInfoEntry
;
646 FILE_INFO_ENTRY
*TempFileInfoEntry
;
647 EFI_FILE_INFO
*FileInfo
;
649 CHAR16
*ListedFileName
;
650 CHAR16
*NewFileNameExtension
;
651 CHAR16
*ListedFileNameExtension
;
652 CHAR16
*TempNewSubStr
;
653 CHAR16
*TempListedSubStr
;
660 UINTN ListedSubStrLen
;
661 INTN SubStrCmpResult
;
663 Status
= EFI_SUCCESS
;
665 ListedFileName
= NULL
;
666 NewFileNameExtension
= NULL
;
667 ListedFileNameExtension
= NULL
;
668 TempNewSubStr
= NULL
;
669 TempListedSubStr
= NULL
;
674 InitializeListHead(FileInfoList
);
676 TempNewSubStr
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
677 TempListedSubStr
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
679 if (TempNewSubStr
== NULL
|| TempListedSubStr
== NULL
) {
680 Status
= EFI_OUT_OF_RESOURCES
;
684 for ( Status
= FileHandleFindFirstFile(Dir
, &FileInfo
)
685 ; !EFI_ERROR(Status
) && !NoFile
686 ; Status
= FileHandleFindNextFile(Dir
, FileInfo
, &NoFile
)
688 if (FileInfo
== NULL
) {
693 // Skip file with mismatching File attribute
695 if ((FileInfo
->Attribute
& (FileAttr
)) == 0) {
699 NewFileInfoEntry
= NULL
;
700 NewFileInfoEntry
= (FILE_INFO_ENTRY
*)AllocateZeroPool(sizeof(FILE_INFO_ENTRY
));
701 if (NewFileInfoEntry
== NULL
) {
702 Status
= EFI_OUT_OF_RESOURCES
;
705 NewFileInfoEntry
->Signature
= FILE_INFO_SIGNATURE
;
706 NewFileInfoEntry
->FileInfo
= AllocateCopyPool((UINTN
) FileInfo
->Size
, FileInfo
);
707 if (NewFileInfoEntry
->FileInfo
== NULL
) {
708 FreePool(NewFileInfoEntry
);
709 Status
= EFI_OUT_OF_RESOURCES
;
713 NewFileInfoEntry
->FileNameFirstPart
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
714 if (NewFileInfoEntry
->FileNameFirstPart
== NULL
) {
715 FreePool(NewFileInfoEntry
->FileInfo
);
716 FreePool(NewFileInfoEntry
);
717 Status
= EFI_OUT_OF_RESOURCES
;
720 NewFileInfoEntry
->FileNameSecondPart
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
721 if (NewFileInfoEntry
->FileNameSecondPart
== NULL
) {
722 FreePool(NewFileInfoEntry
->FileInfo
);
723 FreePool(NewFileInfoEntry
->FileNameFirstPart
);
724 FreePool(NewFileInfoEntry
);
725 Status
= EFI_OUT_OF_RESOURCES
;
730 // Splitter the whole New file name into 2 parts between the last period L'.' into NewFileName NewFileExtension
731 // If no period in the whole file name. NewFileExtension is set to L'\0'
733 NewFileName
= NewFileInfoEntry
->FileNameFirstPart
;
734 NewFileNameExtension
= NewFileInfoEntry
->FileNameSecondPart
;
735 SplitFileNameExtension(FileInfo
->FileName
, NewFileName
, NewFileNameExtension
);
736 UpperCaseString(NewFileName
);
737 UpperCaseString(NewFileNameExtension
);
740 // Insert capsule file in alphabetical ordered list
742 for (Link
= FileInfoList
->ForwardLink
; Link
!= FileInfoList
; Link
= Link
->ForwardLink
) {
744 // Get the FileInfo from the link list
746 TempFileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
747 ListedFileName
= TempFileInfoEntry
->FileNameFirstPart
;
748 ListedFileNameExtension
= TempFileInfoEntry
->FileNameSecondPart
;
751 // Follow rule in UEFI spec 8.5.5 to compare file name
757 // First compare each substrings in NewFileName & ListedFileName between periods
759 GetSubStringBeforePeriod(&NewFileName
[IndexNew
], TempNewSubStr
, &NewSubStrLen
);
760 GetSubStringBeforePeriod(&ListedFileName
[IndexListed
], TempListedSubStr
, &ListedSubStrLen
);
761 if (NewSubStrLen
> ListedSubStrLen
) {
763 // Substr in NewFileName is longer. Pad tail with SPACE
765 PadStrInTail(TempListedSubStr
, NewSubStrLen
- ListedSubStrLen
, L
' ');
766 } else if (NewSubStrLen
< ListedSubStrLen
){
768 // Substr in ListedFileName is longer. Pad tail with SPACE
770 PadStrInTail(TempNewSubStr
, ListedSubStrLen
- NewSubStrLen
, L
' ');
773 SubStrCmpResult
= StrnCmp(TempNewSubStr
, TempListedSubStr
, MAX_FILE_NAME_LEN
);
774 if (SubStrCmpResult
!= 0) {
779 // Move to skip this substring
781 IndexNew
+= NewSubStrLen
;
782 IndexListed
+= ListedSubStrLen
;
784 // Reach File First Name end
786 if (NewFileName
[IndexNew
] == L
'\0' || ListedFileName
[IndexListed
] == L
'\0') {
791 // Skip the period L'.'
797 if (SubStrCmpResult
< 0) {
799 // NewFileName is smaller. Find the right place to insert New file
802 } else if (SubStrCmpResult
== 0) {
804 // 2 cases whole NewFileName is smaller than ListedFileName
805 // 1. if NewFileName == ListedFileName. Continue to compare FileNameExtension
806 // 2. if NewFileName is shorter than ListedFileName
808 if (NewFileName
[IndexNew
] == L
'\0') {
809 if (ListedFileName
[IndexListed
] != L
'\0' || (StrnCmp(NewFileNameExtension
, ListedFileNameExtension
, MAX_FILE_NAME_LEN
) < 0)) {
816 // Other case, ListedFileName is smaller. Continue to compare the next file in the list
821 // If Find an entry in the list whose name is bigger than new FileInfo in alphabet order
822 // Insert it before this entry
824 // Insert at the tail of this list (Link = FileInfoList)
826 InsertTailList(Link
, &NewFileInfoEntry
->Link
);
831 *FileNum
= FileCount
;
835 if (TempNewSubStr
!= NULL
) {
836 FreePool(TempNewSubStr
);
839 if (TempListedSubStr
!= NULL
) {
840 FreePool(TempListedSubStr
);
843 if (EFI_ERROR(Status
)) {
844 while(!IsListEmpty(FileInfoList
)) {
845 Link
= FileInfoList
->ForwardLink
;
846 RemoveEntryList(Link
);
848 TempFileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
850 FreePool(TempFileInfoEntry
->FileInfo
);
851 FreePool(TempFileInfoEntry
->FileNameFirstPart
);
852 FreePool(TempFileInfoEntry
->FileNameSecondPart
);
853 FreePool(TempFileInfoEntry
);
863 This routine is called to get all qualified image from file from an given directory
864 in alphabetic order. All the file image is copied to allocated boottime memory.
865 Caller should free these memory
867 @param[in] Dir Directory file handler
868 @param[in] FileAttr Attribute of file to be red from directory
869 @param[out] FilePtr File images Info buffer red from directory
870 @param[out] FileNum File images number red from directory
872 @retval EFI_SUCCESS Succeed to get all capsules in alphabetic order.
876 GetFileImageInAlphabetFromDir(
877 IN EFI_FILE_HANDLE Dir
,
879 OUT IMAGE_INFO
**FilePtr
,
885 EFI_FILE_HANDLE FileHandle
;
886 FILE_INFO_ENTRY
*FileInfoEntry
;
887 EFI_FILE_INFO
*FileInfo
;
889 IMAGE_INFO
*TempFilePtrBuf
;
891 LIST_ENTRY FileInfoList
;
895 TempFilePtrBuf
= NULL
;
899 // Get file list in Dir in alphabetical order
901 Status
= GetFileInfoListInAlphabetFromDir(
907 if (EFI_ERROR(Status
)) {
908 DEBUG ((DEBUG_ERROR
, "GetFileInfoListInAlphabetFromDir Failed!\n"));
912 if (FileCount
== 0) {
913 DEBUG ((DEBUG_ERROR
, "No file found in Dir!\n"));
914 Status
= EFI_NOT_FOUND
;
918 TempFilePtrBuf
= (IMAGE_INFO
*)AllocateZeroPool(sizeof(IMAGE_INFO
) * FileCount
);
919 if (TempFilePtrBuf
== NULL
) {
920 Status
= EFI_OUT_OF_RESOURCES
;
925 // Read all files from FileInfoList to BS memory
928 for (Link
= FileInfoList
.ForwardLink
; Link
!= &FileInfoList
; Link
= Link
->ForwardLink
) {
930 // Get FileInfo from the link list
932 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
933 FileInfo
= FileInfoEntry
->FileInfo
;
942 if (EFI_ERROR(Status
)){
946 Size
= (UINTN
)FileInfo
->FileSize
;
947 TempFilePtrBuf
[FileCount
].ImageAddress
= AllocateZeroPool(Size
);
948 if (TempFilePtrBuf
[FileCount
].ImageAddress
== NULL
) {
949 DEBUG((DEBUG_ERROR
, "Fail to allocate memory for capsule. Stop processing the rest.\n"));
953 Status
= FileHandle
->Read(
956 TempFilePtrBuf
[FileCount
].ImageAddress
959 FileHandle
->Close(FileHandle
);
962 // Skip read error file
964 if (EFI_ERROR(Status
) || Size
!= (UINTN
)FileInfo
->FileSize
) {
966 // Remove this error file info accordingly
967 // & move Link to BackLink
969 Link
= RemoveEntryList(Link
);
970 Link
= Link
->BackLink
;
972 FreePool(FileInfoEntry
->FileInfo
);
973 FreePool(FileInfoEntry
->FileNameFirstPart
);
974 FreePool(FileInfoEntry
->FileNameSecondPart
);
975 FreePool(FileInfoEntry
);
977 FreePool(TempFilePtrBuf
[FileCount
].ImageAddress
);
978 TempFilePtrBuf
[FileCount
].ImageAddress
= NULL
;
979 TempFilePtrBuf
[FileCount
].FileInfo
= NULL
;
983 TempFilePtrBuf
[FileCount
].FileInfo
= FileInfo
;
988 for (Link
= FileInfoList
.ForwardLink
; Link
!= &FileInfoList
; Link
= Link
->ForwardLink
) {
989 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
990 FileInfo
= FileInfoEntry
->FileInfo
;
991 DEBUG((DEBUG_INFO
, "Successfully read capsule file %s from disk.\n", FileInfo
->FileName
));
997 *FilePtr
= TempFilePtrBuf
;
998 *FileNum
= FileCount
;
1001 // FileInfo will be freed by Calller
1003 while(!IsListEmpty(&FileInfoList
)) {
1004 Link
= FileInfoList
.ForwardLink
;
1005 RemoveEntryList(Link
);
1007 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
1009 FreePool(FileInfoEntry
->FileNameFirstPart
);
1010 FreePool(FileInfoEntry
->FileNameSecondPart
);
1011 FreePool(FileInfoEntry
);
1018 This routine is called to remove all qualified image from file from an given directory.
1020 @param[in] Dir Directory file handler
1021 @param[in] FileAttr Attribute of files to be deleted
1023 @retval EFI_SUCCESS Succeed to remove all files from an given directory.
1028 IN EFI_FILE_HANDLE Dir
,
1034 LIST_ENTRY FileInfoList
;
1035 EFI_FILE_HANDLE FileHandle
;
1036 FILE_INFO_ENTRY
*FileInfoEntry
;
1037 EFI_FILE_INFO
*FileInfo
;
1043 // Get file list in Dir in alphabetical order
1045 Status
= GetFileInfoListInAlphabetFromDir(
1051 if (EFI_ERROR(Status
)) {
1052 DEBUG ((DEBUG_ERROR
, "GetFileInfoListInAlphabetFromDir Failed!\n"));
1056 if (FileCount
== 0) {
1057 DEBUG ((DEBUG_ERROR
, "No file found in Dir!\n"));
1058 Status
= EFI_NOT_FOUND
;
1063 // Delete all files with given attribute in Dir
1065 for (Link
= FileInfoList
.ForwardLink
; Link
!= &(FileInfoList
); Link
= Link
->ForwardLink
) {
1067 // Get FileInfo from the link list
1069 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
1070 FileInfo
= FileInfoEntry
->FileInfo
;
1076 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
,
1079 if (EFI_ERROR(Status
)){
1083 Status
= FileHandle
->Delete(FileHandle
);
1088 while(!IsListEmpty(&FileInfoList
)) {
1089 Link
= FileInfoList
.ForwardLink
;
1090 RemoveEntryList(Link
);
1092 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
1094 FreePool(FileInfoEntry
->FileInfo
);
1095 FreePool(FileInfoEntry
);
1102 This routine is called to get all caspules from file. The capsule file image is
1103 copied to BS memory. Caller is responsible to free them.
1105 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1106 devices like USB can get enumerated.
1107 @param[out] CapsulePtr Copied Capsule file Image Info buffer
1108 @param[out] CapsuleNum CapsuleNumber
1109 @param[out] FsHandle File system handle
1110 @param[out] LoadOptionNumber OptionNumber of boot option
1112 @retval EFI_SUCCESS Succeed to get all capsules.
1116 GetAllCapsuleOnDisk(
1118 OUT IMAGE_INFO
**CapsulePtr
,
1119 OUT UINTN
*CapsuleNum
,
1120 OUT EFI_HANDLE
*FsHandle
,
1121 OUT UINT16
*LoadOptionNumber
1125 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
1126 EFI_FILE_HANDLE RootDir
;
1127 EFI_FILE_HANDLE FileDir
;
1128 UINT16
*TempOptionNumber
;
1130 TempOptionNumber
= NULL
;
1133 Status
= GetEfiSysPartitionFromActiveBootOption(MaxRetry
, &TempOptionNumber
, FsHandle
);
1134 if (EFI_ERROR(Status
)) {
1138 Status
= gBS
->HandleProtocol(*FsHandle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
1139 if (EFI_ERROR(Status
)) {
1143 Status
= Fs
->OpenVolume(Fs
, &RootDir
);
1144 if (EFI_ERROR(Status
)) {
1148 Status
= RootDir
->Open(
1151 EFI_CAPSULE_FILE_DIRECTORY
,
1155 if (EFI_ERROR(Status
)) {
1156 DEBUG((DEBUG_ERROR
, "CodLibGetAllCapsuleOnDisk fail to open RootDir!\n"));
1157 RootDir
->Close (RootDir
);
1160 RootDir
->Close (RootDir
);
1163 // Only Load files with EFI_FILE_SYSTEM or EFI_FILE_ARCHIVE attribute
1164 // ignore EFI_FILE_READ_ONLY, EFI_FILE_HIDDEN, EFI_FILE_RESERVED, EFI_FILE_DIRECTORY
1166 Status
= GetFileImageInAlphabetFromDir(
1168 EFI_FILE_SYSTEM
| EFI_FILE_ARCHIVE
,
1172 DEBUG((DEBUG_INFO
, "GetFileImageInAlphabetFromDir status %x\n", Status
));
1175 // Always remove file to avoid deadloop in capsule process
1177 Status
= RemoveFileFromDir(FileDir
, EFI_FILE_SYSTEM
| EFI_FILE_ARCHIVE
);
1178 DEBUG((DEBUG_INFO
, "RemoveFileFromDir status %x\n", Status
));
1180 FileDir
->Close (FileDir
);
1182 if (LoadOptionNumber
!= NULL
) {
1183 *LoadOptionNumber
= *TempOptionNumber
;
1190 Build Gather list for a list of capsule images.
1192 @param[in] CapsuleBuffer An array of pointer to capsule images
1193 @param[in] CapsuleSize An array of UINTN to capsule images size
1194 @param[in] CapsuleNum The count of capsule images
1195 @param[out] BlockDescriptors The block descriptors for the capsule images
1197 @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.
1202 IN VOID
**CapsuleBuffer
,
1203 IN UINTN
*CapsuleSize
,
1204 IN UINTN CapsuleNum
,
1205 OUT EFI_CAPSULE_BLOCK_DESCRIPTOR
**BlockDescriptors
1209 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptors1
;
1210 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptorPre
;
1211 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptorsHeader
;
1214 BlockDescriptors1
= NULL
;
1215 BlockDescriptorPre
= NULL
;
1216 BlockDescriptorsHeader
= NULL
;
1218 for (Index
= 0; Index
< CapsuleNum
; Index
++) {
1220 // Allocate memory for the descriptors.
1222 BlockDescriptors1
= AllocateZeroPool (2 * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR
));
1223 if (BlockDescriptors1
== NULL
) {
1224 DEBUG ((DEBUG_ERROR
, "BuildGatherList: failed to allocate memory for descriptors\n"));
1225 Status
= EFI_OUT_OF_RESOURCES
;
1228 DEBUG ((DEBUG_INFO
, "BuildGatherList: creating capsule descriptors at 0x%X\n", (UINTN
) BlockDescriptors1
));
1232 // Record descirptor header
1235 BlockDescriptorsHeader
= BlockDescriptors1
;
1238 if (BlockDescriptorPre
!= NULL
) {
1239 BlockDescriptorPre
->Union
.ContinuationPointer
= (UINTN
) BlockDescriptors1
;
1240 BlockDescriptorPre
->Length
= 0;
1243 BlockDescriptors1
->Union
.DataBlock
= (UINTN
) CapsuleBuffer
[Index
];
1244 BlockDescriptors1
->Length
= CapsuleSize
[Index
];
1246 BlockDescriptorPre
= BlockDescriptors1
+ 1;
1247 BlockDescriptors1
= NULL
;
1253 if (BlockDescriptorPre
!= NULL
) {
1254 BlockDescriptorPre
->Union
.ContinuationPointer
= (UINTN
)NULL
;
1255 BlockDescriptorPre
->Length
= 0;
1256 *BlockDescriptors
= BlockDescriptorsHeader
;
1262 if (BlockDescriptors1
!= NULL
) {
1263 FreePool (BlockDescriptors1
);
1270 This routine is called to check if CapsuleOnDisk flag in OsIndications Variable
1273 @retval TRUE Flag is enabled
1274 @retval FALSE Flag is not enabled
1279 CoDCheckCapsuleOnDiskFlag(
1284 UINT64 OsIndication
;
1288 // Check File Capsule Delivery Supported Flag in OsIndication variable
1291 DataSize
= sizeof(UINT64
);
1292 Status
= gRT
->GetVariable (
1293 EFI_OS_INDICATIONS_VARIABLE_NAME
,
1294 &gEfiGlobalVariableGuid
,
1299 if (!EFI_ERROR(Status
) &&
1300 (OsIndication
& EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
) != 0) {
1309 This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable.
1311 @retval EFI_SUCCESS All Capsule On Disk flags are cleared
1316 CoDClearCapsuleOnDiskFlag(
1321 UINT64 OsIndication
;
1325 // Reset File Capsule Delivery Supported Flag in OsIndication variable
1328 DataSize
= sizeof(UINT64
);
1329 Status
= gRT
->GetVariable (
1330 EFI_OS_INDICATIONS_VARIABLE_NAME
,
1331 &gEfiGlobalVariableGuid
,
1336 if (EFI_ERROR(Status
) ||
1337 (OsIndication
& EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
) == 0) {
1341 OsIndication
&= ~((UINT64
)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
);
1342 Status
= gRT
->SetVariable (
1343 EFI_OS_INDICATIONS_VARIABLE_NAME
,
1344 &gEfiGlobalVariableGuid
,
1345 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
1349 ASSERT(!EFI_ERROR(Status
));
1352 // Delete BootNext variable. Capsule Process may reset system, so can't rely on Bds to clear this variable
1354 Status
= gRT
->SetVariable (
1355 EFI_BOOT_NEXT_VARIABLE_NAME
,
1356 &gEfiGlobalVariableGuid
,
1361 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_NOT_FOUND
);
1367 This routine is called to clear CapsuleOnDisk Relocation Info variable.
1368 Total Capsule On Disk length is recorded in this variable
1370 @retval EFI_SUCCESS Capsule On Disk flags are cleared
1374 CoDClearCapsuleRelocationInfo(
1378 return gRT
->SetVariable (
1379 COD_RELOCATION_INFO_VAR_NAME
,
1380 &gEfiCapsuleVendorGuid
,
1388 Relocate Capsule on Disk from EFI system partition to a platform-specific NV storage device
1389 with BlockIo protocol. Relocation device path, identified by PcdCodRelocationDevPath, must
1390 be a full device path.
1391 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1392 Function will stall 100ms between each retry.
1395 Content corruption. Block IO write directly touches low level write. Orignal partitions, file systems
1396 of the relocation device will be corrupted.
1398 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1399 devices like USB can get enumerated.
1401 @retval EFI_SUCCESS Capsule on Disk images are sucessfully relocated to the platform-specific device.
1405 RelocateCapsuleToDisk(
1410 UINTN CapsuleOnDiskNum
;
1413 UINT64 TotalImageSize
;
1414 UINT64 TotalImageNameSize
;
1415 IMAGE_INFO
*CapsuleOnDiskBuf
;
1417 EFI_HANDLE TempHandle
;
1418 EFI_HANDLE
*HandleBuffer
;
1419 UINTN NumberOfHandles
;
1420 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
1421 UINT8
*CapsuleDataBuf
;
1423 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
1424 EFI_FILE_HANDLE RootDir
;
1425 EFI_FILE_HANDLE TempCodFile
;
1426 UINT64 TempCodFileSize
;
1427 EFI_DEVICE_PATH
*TempDevicePath
;
1428 BOOLEAN RelocationInfo
;
1429 UINT16 LoadOptionNumber
;
1430 EFI_CAPSULE_HEADER FileNameCapsuleHeader
;
1434 HandleBuffer
= NULL
;
1435 CapsuleDataBuf
= NULL
;
1436 CapsuleOnDiskBuf
= NULL
;
1437 NumberOfHandles
= 0;
1439 DEBUG ((DEBUG_INFO
, "CapsuleOnDisk RelocateCapsule Enter\n"));
1442 // 1. Load all Capsule On Disks in to memory
1444 Status
= GetAllCapsuleOnDisk(MaxRetry
, &CapsuleOnDiskBuf
, &CapsuleOnDiskNum
, &Handle
, &LoadOptionNumber
);
1445 if (EFI_ERROR(Status
) || CapsuleOnDiskNum
== 0 || CapsuleOnDiskBuf
== NULL
) {
1446 DEBUG ((DEBUG_INFO
, "RelocateCapsule: GetAllCapsuleOnDisk Status - 0x%x\n", Status
));
1447 return EFI_NOT_FOUND
;
1451 // 2. Connect platform special device path as relocation device.
1452 // If no platform special device path specified or the device path is invalid, use the EFI system partition where
1453 // stores the capsules as relocation device.
1455 if (IsDevicePathValid ((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
), PcdGetSize(PcdCodRelocationDevPath
))) {
1456 Status
= EfiBootManagerConnectDevicePath ((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
), &TempHandle
);
1457 if (EFI_ERROR(Status
)) {
1458 DEBUG ((DEBUG_ERROR
, "RelocateCapsule: EfiBootManagerConnectDevicePath Status - 0x%x\n", Status
));
1463 // Connect all the child handle. Partition & FAT drivers are allowed in this case
1465 gBS
->ConnectController (TempHandle
, NULL
, NULL
, TRUE
);
1466 Status
= gBS
->LocateHandleBuffer(
1468 &gEfiSimpleFileSystemProtocolGuid
,
1473 if (EFI_ERROR(Status
)) {
1474 DEBUG ((DEBUG_ERROR
, "RelocateCapsule: LocateHandleBuffer Status - 0x%x\n", Status
));
1479 // Find first Simple File System Handle which can match PcdCodRelocationDevPath
1481 for (Index
= 0; Index
< NumberOfHandles
; Index
++) {
1482 Status
= gBS
->HandleProtocol(HandleBuffer
[Index
], &gEfiDevicePathProtocolGuid
, (VOID
**)&TempDevicePath
);
1483 if (EFI_ERROR(Status
)) {
1487 DataSize
= GetDevicePathSize((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
)) - sizeof(EFI_DEVICE_PATH
);
1488 if (0 == CompareMem((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
), TempDevicePath
, DataSize
)) {
1489 Handle
= HandleBuffer
[Index
];
1494 FreePool(HandleBuffer
);
1496 if (Index
== NumberOfHandles
) {
1497 DEBUG ((DEBUG_ERROR
, "RelocateCapsule: No simple file system protocol found.\n"));
1498 Status
= EFI_NOT_FOUND
;
1502 Status
= gBS
->HandleProtocol(Handle
, &gEfiBlockIoProtocolGuid
, (VOID
**)&BlockIo
);
1503 if (EFI_ERROR(Status
) || BlockIo
->Media
->ReadOnly
) {
1504 DEBUG((DEBUG_ERROR
, "Fail to find Capsule on Disk relocation BlockIo device or device is ReadOnly!\n"));
1508 Status
= gBS
->HandleProtocol(Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
1509 if (EFI_ERROR(Status
)) {
1514 // Check if device used to relocate Capsule On Disk is big enough
1517 TotalImageNameSize
= 0;
1518 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1522 if (MAX_ADDRESS
- (UINTN
)TotalImageSize
<= CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
) {
1523 Status
= EFI_INVALID_PARAMETER
;
1527 if (MAX_ADDRESS
- (UINTN
)TotalImageNameSize
<= StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
)) {
1528 Status
= EFI_INVALID_PARAMETER
;
1532 TotalImageSize
+= CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
;
1533 TotalImageNameSize
+= StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1534 DEBUG((DEBUG_INFO
, "RelocateCapsule: %x Size %x\n",CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
, CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
));
1537 DEBUG((DEBUG_INFO
, "RelocateCapsule: TotalImageSize %x\n", TotalImageSize
));
1538 DEBUG((DEBUG_INFO
, "RelocateCapsule: TotalImageNameSize %x\n", TotalImageNameSize
));
1540 if (MAX_ADDRESS
- (UINTN
)TotalImageNameSize
<= sizeof(UINT64
) * 2 ||
1541 MAX_ADDRESS
- (UINTN
)TotalImageSize
<= (UINTN
)TotalImageNameSize
+ sizeof(UINT64
) * 2) {
1542 Status
= EFI_INVALID_PARAMETER
;
1546 TempCodFileSize
= sizeof(UINT64
) + TotalImageSize
+ sizeof(EFI_CAPSULE_HEADER
) + TotalImageNameSize
;
1549 // Check if CapsuleTotalSize. There could be reminder, so use LastBlock number directly
1551 if (DivU64x32(TempCodFileSize
, BlockIo
->Media
->BlockSize
) > BlockIo
->Media
->LastBlock
) {
1552 DEBUG((DEBUG_ERROR
, "RelocateCapsule: Relocation device isn't big enough to hold all Capsule on Disk!\n"));
1553 DEBUG((DEBUG_ERROR
, "TotalImageSize = %x\n", TotalImageSize
));
1554 DEBUG((DEBUG_ERROR
, "TotalImageNameSize = %x\n", TotalImageNameSize
));
1555 DEBUG((DEBUG_ERROR
, "RelocationDev BlockSize = %x LastBlock = %x\n", BlockIo
->Media
->BlockSize
, BlockIo
->Media
->LastBlock
));
1556 Status
= EFI_OUT_OF_RESOURCES
;
1560 CapsuleDataBuf
= AllocatePool((UINTN
) TempCodFileSize
);
1561 if (CapsuleDataBuf
== NULL
) {
1562 Status
= EFI_OUT_OF_RESOURCES
;
1567 // First UINT64 reserved for total image size, including capsule name capsule.
1569 *(UINT64
*) CapsuleDataBuf
= TotalImageSize
+ sizeof(EFI_CAPSULE_HEADER
) + TotalImageNameSize
;
1572 // Line up all the Capsule on Disk and write to relocation disk at one time. It could save some time in disk write
1574 for (Index
= 0, CapsulePtr
= CapsuleDataBuf
+ sizeof(UINT64
); Index
< CapsuleOnDiskNum
; Index
++) {
1575 CopyMem(CapsulePtr
, CapsuleOnDiskBuf
[Index
].ImageAddress
, (UINTN
) CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
);
1576 CapsulePtr
+= CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
;
1580 // Line the capsule header for capsule name capsule.
1582 CopyGuid(&FileNameCapsuleHeader
.CapsuleGuid
, &gEdkiiCapsuleOnDiskNameGuid
);
1583 FileNameCapsuleHeader
.CapsuleImageSize
= (UINT32
) TotalImageNameSize
+ sizeof(EFI_CAPSULE_HEADER
);
1584 FileNameCapsuleHeader
.Flags
= CAPSULE_FLAGS_PERSIST_ACROSS_RESET
;
1585 FileNameCapsuleHeader
.HeaderSize
= sizeof(EFI_CAPSULE_HEADER
);
1586 CopyMem(CapsulePtr
, &FileNameCapsuleHeader
, FileNameCapsuleHeader
.HeaderSize
);
1587 CapsulePtr
+= FileNameCapsuleHeader
.HeaderSize
;
1590 // Line up all the Capsule file names.
1592 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1593 CopyMem(CapsulePtr
, CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
, StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
));
1594 CapsulePtr
+= StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1598 // 5. Flash all Capsules on Disk to TempCoD.tmp under RootDir
1600 Status
= Fs
->OpenVolume(Fs
, &RootDir
);
1601 if (EFI_ERROR(Status
)) {
1602 DEBUG((DEBUG_ERROR
, "RelocateCapsule: OpenVolume error. %x\n", Status
));
1606 Status
= RootDir
->Open(
1609 (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
),
1610 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
,
1613 if (!EFI_ERROR(Status
)) {
1615 // Error handling code to prevent malicious code to hold this file to block capsule on disk
1617 TempCodFile
->Delete(TempCodFile
);
1619 Status
= RootDir
->Open(
1622 (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
),
1623 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
,
1626 if (EFI_ERROR(Status
)) {
1627 DEBUG((DEBUG_ERROR
, "RelocateCapsule: Open TemCoD.tmp error. %x\n", Status
));
1632 // Always write at the begining of TempCap file
1634 DataSize
= (UINTN
) TempCodFileSize
;
1635 Status
= TempCodFile
->Write(
1640 if (EFI_ERROR(Status
)) {
1641 DEBUG((DEBUG_ERROR
, "RelocateCapsule: Write TemCoD.tmp error. %x\n", Status
));
1645 if (DataSize
!= TempCodFileSize
) {
1646 Status
= EFI_DEVICE_ERROR
;
1651 // Save Capsule On Disk relocation info to "CodRelocationInfo" Var
1652 // It is used in next reboot by TCB
1654 RelocationInfo
= TRUE
;
1655 Status
= gRT
->SetVariable(
1656 COD_RELOCATION_INFO_VAR_NAME
,
1657 &gEfiCapsuleVendorGuid
,
1658 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
1663 // Save the LoadOptionNumber of the boot option, where the capsule is relocated,
1664 // into "CodRelocationLoadOption" var. It is used in next reboot after capsule is
1665 // updated out of TCB to remove the TempCoDFile.
1667 Status
= gRT
->SetVariable(
1668 COD_RELOCATION_LOAD_OPTION_VAR_NAME
,
1669 &gEfiCapsuleVendorGuid
,
1670 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
1677 if (CapsuleDataBuf
!= NULL
) {
1678 FreePool(CapsuleDataBuf
);
1681 if (CapsuleOnDiskBuf
!= NULL
) {
1683 // Free resources allocated by CodLibGetAllCapsuleOnDisk
1685 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++ ) {
1686 FreePool(CapsuleOnDiskBuf
[Index
].ImageAddress
);
1687 FreePool(CapsuleOnDiskBuf
[Index
].FileInfo
);
1689 FreePool(CapsuleOnDiskBuf
);
1692 if (TempCodFile
!= NULL
) {
1693 if (EFI_ERROR(Status
)) {
1694 TempCodFile
->Delete (TempCodFile
);
1696 TempCodFile
->Close (TempCodFile
);
1700 if (RootDir
!= NULL
) {
1701 RootDir
->Close (RootDir
);
1708 For the platforms that support Capsule In Ram, reuse the Capsule In Ram to deliver capsule.
1709 Relocate Capsule On Disk to memory and call UpdateCapsule().
1710 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1711 Function will stall 100ms between each retry.
1713 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1714 devices like USB can get enumerated.
1716 @retval EFI_SUCCESS Deliver capsule through Capsule In Ram successfully.
1720 RelocateCapsuleToRam (
1725 UINTN CapsuleOnDiskNum
;
1726 IMAGE_INFO
*CapsuleOnDiskBuf
;
1728 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptors
;
1729 VOID
**CapsuleBuffer
;
1731 EFI_CAPSULE_HEADER
*FileNameCapsule
;
1735 UINTN TotalStringSize
;
1737 CapsuleOnDiskBuf
= NULL
;
1738 BlockDescriptors
= NULL
;
1739 CapsuleBuffer
= NULL
;
1741 FileNameCapsule
= NULL
;
1742 TotalStringSize
= 0;
1745 // 1. Load all Capsule On Disks into memory
1747 Status
= GetAllCapsuleOnDisk (MaxRetry
, &CapsuleOnDiskBuf
, &CapsuleOnDiskNum
, &Handle
, NULL
);
1748 if (EFI_ERROR (Status
) || CapsuleOnDiskNum
== 0 || CapsuleOnDiskBuf
== NULL
) {
1749 DEBUG ((DEBUG_ERROR
, "GetAllCapsuleOnDisk Status - 0x%x\n", Status
));
1750 return EFI_NOT_FOUND
;
1754 // 2. Add a capsule for Capsule file name strings
1756 CapsuleBuffer
= AllocateZeroPool ((CapsuleOnDiskNum
+ 1) * sizeof (VOID
*));
1757 if (CapsuleBuffer
== NULL
) {
1758 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory for capsules.\n"));
1759 return EFI_OUT_OF_RESOURCES
;
1762 CapsuleSize
= AllocateZeroPool ((CapsuleOnDiskNum
+ 1) * sizeof (UINTN
));
1763 if (CapsuleSize
== NULL
) {
1764 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory for capsules.\n"));
1765 FreePool (CapsuleBuffer
);
1766 return EFI_OUT_OF_RESOURCES
;
1769 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1770 CapsuleBuffer
[Index
] = (VOID
*)(UINTN
) CapsuleOnDiskBuf
[Index
].ImageAddress
;
1771 CapsuleSize
[Index
] = (UINTN
) CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
;
1772 TotalStringSize
+= StrSize (CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1775 FileNameCapsule
= AllocateZeroPool (sizeof (EFI_CAPSULE_HEADER
) + TotalStringSize
);
1776 if (FileNameCapsule
== NULL
) {
1777 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory for name capsule.\n"));
1778 FreePool (CapsuleBuffer
);
1779 FreePool (CapsuleSize
);
1780 return EFI_OUT_OF_RESOURCES
;
1783 FileNameCapsule
->CapsuleImageSize
= (UINT32
) (sizeof (EFI_CAPSULE_HEADER
) + TotalStringSize
);
1784 FileNameCapsule
->Flags
= CAPSULE_FLAGS_PERSIST_ACROSS_RESET
;
1785 FileNameCapsule
->HeaderSize
= sizeof (EFI_CAPSULE_HEADER
);
1786 CopyGuid (&(FileNameCapsule
->CapsuleGuid
), &gEdkiiCapsuleOnDiskNameGuid
);
1788 StringBuf
= (UINT8
*)FileNameCapsule
+ FileNameCapsule
->HeaderSize
;
1789 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1790 StringSize
= StrSize (CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1791 CopyMem (StringBuf
, CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
, StringSize
);
1792 StringBuf
+= StringSize
;
1795 CapsuleBuffer
[CapsuleOnDiskNum
] = FileNameCapsule
;
1796 CapsuleSize
[CapsuleOnDiskNum
] = TotalStringSize
+ sizeof (EFI_CAPSULE_HEADER
);
1799 // 3. Build Gather list for the capsules
1801 Status
= BuildGatherList (CapsuleBuffer
, CapsuleSize
, CapsuleOnDiskNum
+ 1, &BlockDescriptors
);
1802 if (EFI_ERROR (Status
) || BlockDescriptors
== NULL
) {
1803 FreePool (CapsuleBuffer
);
1804 FreePool (CapsuleSize
);
1805 FreePool (FileNameCapsule
);
1806 return EFI_OUT_OF_RESOURCES
;
1810 // 4. Call UpdateCapsule() service
1812 Status
= gRT
->UpdateCapsule((EFI_CAPSULE_HEADER
**) CapsuleBuffer
, CapsuleOnDiskNum
+ 1, (UINTN
) BlockDescriptors
);
1818 Relocate Capsule on Disk from EFI system partition.
1820 Two solution to deliver Capsule On Disk:
1821 Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().
1822 Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage
1823 device with BlockIo protocol.
1825 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1826 Function will stall 100ms between each retry.
1829 Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.
1830 Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file
1831 systems of the relocation device will be corrupted.
1833 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1834 devices like USB can get enumerated. Input 0 means no retry.
1836 @retval EFI_SUCCESS Capsule on Disk images are successfully relocated.
1845 if (!PcdGetBool (PcdCapsuleOnDiskSupport
)) {
1846 return EFI_UNSUPPORTED
;
1850 // Clear CapsuleOnDisk Flag firstly.
1852 CoDClearCapsuleOnDiskFlag ();
1855 // If Capsule In Ram is supported, delivery capsules through memory
1857 if (PcdGetBool (PcdCapsuleInRamSupport
)) {
1858 DEBUG ((DEBUG_INFO
, "Capsule In Ram is supported, call gRT->UpdateCapsule().\n"));
1859 return RelocateCapsuleToRam (MaxRetry
);
1861 DEBUG ((DEBUG_INFO
, "Reallcoate all Capsule on Disks to %s in RootDir.\n", (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
)));
1862 return RelocateCapsuleToDisk (MaxRetry
);
1867 Remove the temp file from the root of EFI System Partition.
1868 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1869 Function will stall 100ms between each retry.
1871 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1872 devices like USB can get enumerated. Input 0 means no retry.
1874 @retval EFI_SUCCESS Remove the temp file successfully.
1885 UINT16
*LoadOptionNumber
;
1886 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
1887 EFI_HANDLE FsHandle
;
1888 EFI_FILE_HANDLE RootDir
;
1889 EFI_FILE_HANDLE TempCodFile
;
1893 DataSize
= sizeof(UINT16
);
1895 LoadOptionNumber
= AllocatePool (sizeof(UINT16
));
1896 if (LoadOptionNumber
== NULL
) {
1897 return EFI_OUT_OF_RESOURCES
;
1901 // Check if capsule files are relocated
1903 Status
= gRT
->GetVariable (
1904 COD_RELOCATION_LOAD_OPTION_VAR_NAME
,
1905 &gEfiCapsuleVendorGuid
,
1908 (VOID
*)LoadOptionNumber
1910 if (EFI_ERROR(Status
) || DataSize
!= sizeof(UINT16
)) {
1915 // Get the EFI file system from the boot option where the capsules are relocated
1917 Status
= GetEfiSysPartitionFromActiveBootOption(MaxRetry
, &LoadOptionNumber
, &FsHandle
);
1918 if (EFI_ERROR(Status
)) {
1922 Status
= gBS
->HandleProtocol(FsHandle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
1923 if (EFI_ERROR(Status
)) {
1927 Status
= Fs
->OpenVolume(Fs
, &RootDir
);
1928 if (EFI_ERROR(Status
)) {
1933 // Delete the TempCoDFile
1935 Status
= RootDir
->Open(
1938 (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
),
1939 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
,
1942 if (EFI_ERROR(Status
)) {
1946 TempCodFile
->Delete(TempCodFile
);
1949 // Clear "CoDRelocationLoadOption" variable
1951 Status
= gRT
->SetVariable (
1952 COD_RELOCATION_LOAD_OPTION_VAR_NAME
,
1953 &gEfiCapsuleVendorGuid
,
1960 if (LoadOptionNumber
!= NULL
) {
1961 FreePool (LoadOptionNumber
);
1964 if (RootDir
!= NULL
) {
1965 RootDir
->Close(RootDir
);