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 CopyMem (*OptionBuf
, &BootOption
, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION
));
355 Get Active EFI System Partition within GPT based on device path.
357 @param[in] DevicePath Device path to find a active EFI System Partition
358 @param[out] FsHandle BootList points to all boot options returned
360 @retval EFI_SUCCESS Active EFI System Partition is succesfully found
361 @retval EFI_NOT_FOUND No Active EFI System Partition is found
365 GetEfiSysPartitionFromDevPath(
366 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
367 OUT EFI_HANDLE
*FsHandle
371 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePath
;
372 HARDDRIVE_DEVICE_PATH
*Hd
;
374 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
377 // Check if the device path contains GPT node
379 TempDevicePath
= DevicePath
;
380 while (!IsDevicePathEnd (TempDevicePath
)) {
381 if ((DevicePathType (TempDevicePath
) == MEDIA_DEVICE_PATH
) &&
382 (DevicePathSubType (TempDevicePath
) == MEDIA_HARDDRIVE_DP
)) {
383 Hd
= (HARDDRIVE_DEVICE_PATH
*)TempDevicePath
;
384 if (Hd
->MBRType
== MBR_TYPE_EFI_PARTITION_TABLE_HEADER
) {
388 TempDevicePath
= NextDevicePathNode (TempDevicePath
);
391 if (!IsDevicePathEnd (TempDevicePath
)) {
393 // Search for EFI system partition protocol on full device path in Boot Option
395 Status
= gBS
->LocateDevicePath (&gEfiPartTypeSystemPartGuid
, &DevicePath
, &Handle
);
398 // Search for simple file system on this handler
400 if (!EFI_ERROR(Status
)) {
401 Status
= gBS
->HandleProtocol(Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
402 if (!EFI_ERROR(Status
)) {
409 return EFI_NOT_FOUND
;
413 This routine is called to get Simple File System protocol on the first EFI system partition found in
414 active boot option. The boot option list is detemined in order by
418 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
419 device like USB can get enumerated.
420 @param[in, out] LoadOptionNumber On input, specify the boot option to get EFI system partition.
421 On output, return the OptionNumber of the boot option where EFI
422 system partition is got from.
423 @param[out] FsFsHandle Simple File System Protocol found on first active EFI system partition
425 @retval EFI_SUCCESS Simple File System protocol found for EFI system partition
426 @retval EFI_NOT_FOUND No Simple File System protocol found for EFI system partition
430 GetEfiSysPartitionFromActiveBootOption(
432 IN OUT UINT16
**LoadOptionNumber
,
433 OUT EFI_HANDLE
*FsHandle
437 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptionBuf
;
440 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
441 EFI_DEVICE_PATH_PROTOCOL
*CurFullPath
;
442 EFI_DEVICE_PATH_PROTOCOL
*PreFullPath
;
446 if (*LoadOptionNumber
!= NULL
) {
448 Status
= GetBootOptionByNumber(**LoadOptionNumber
, &BootOptionBuf
);
449 if (EFI_ERROR(Status
)) {
450 DEBUG ((DEBUG_ERROR
, "GetBootOptionByIndex Failed %x! No BootOption available for connection\n", Status
));
454 Status
= GetBootOptionInOrder(&BootOptionBuf
, &BootOptionNum
);
455 if (EFI_ERROR(Status
)) {
456 DEBUG ((DEBUG_ERROR
, "GetBootOptionInOrder Failed %x! No BootOption available for connection\n", Status
));
462 // Search BootOptionList to check if it is an active boot option with EFI system partition
463 // 1. Connect device path
464 // 2. expend short/plug in devicepath
467 for (Index
= 0; Index
< BootOptionNum
; Index
++) {
469 // Get the boot option from the link list
471 DevicePath
= BootOptionBuf
[Index
].FilePath
;
474 // Skip inactive or legacy boot options
476 if ((BootOptionBuf
[Index
].Attributes
& LOAD_OPTION_ACTIVE
) == 0 ||
477 DevicePathType (DevicePath
) == BBS_DEVICE_PATH
) {
482 CHAR16
*DevicePathStr
;
484 DevicePathStr
= ConvertDevicePathToText(DevicePath
, TRUE
, TRUE
);
485 if (DevicePathStr
!= NULL
){
486 DEBUG((DEBUG_INFO
, "Try BootOption %s\n", DevicePathStr
));
487 FreePool(DevicePathStr
);
489 DEBUG((DEBUG_INFO
, "DevicePathToStr failed\n"));
495 // Try every full device Path generated from bootoption
498 PreFullPath
= CurFullPath
;
499 CurFullPath
= EfiBootManagerGetNextLoadOptionDevicePath(DevicePath
, CurFullPath
);
501 if (PreFullPath
!= NULL
) {
502 FreePool (PreFullPath
);
505 if (CurFullPath
== NULL
) {
507 // No Active EFI system partition is found in BootOption device path
509 Status
= EFI_NOT_FOUND
;
514 CHAR16
*DevicePathStr1
;
516 DevicePathStr1
= ConvertDevicePathToText(CurFullPath
, TRUE
, TRUE
);
517 if (DevicePathStr1
!= NULL
){
518 DEBUG((DEBUG_INFO
, "Full device path %s\n", DevicePathStr1
));
519 FreePool(DevicePathStr1
);
524 // Make sure the boot option device path connected.
525 // Only handle first device in boot option. Other optional device paths are described as OSV specific
526 // FullDevice could contain extra directory & file info. So don't check connection status here.
528 EfiBootManagerConnectDevicePath (CurFullPath
, NULL
);
529 Status
= GetEfiSysPartitionFromDevPath(CurFullPath
, FsHandle
);
532 // Some relocation device like USB need more time to get enumerated
534 while (EFI_ERROR(Status
) && MaxRetry
> 0) {
535 EfiBootManagerConnectDevicePath(CurFullPath
, NULL
);
538 // Search for EFI system partition protocol on full device path in Boot Option
540 Status
= GetEfiSysPartitionFromDevPath(CurFullPath
, FsHandle
);
541 if (!EFI_ERROR(Status
)) {
544 DEBUG((DEBUG_ERROR
, "GetEfiSysPartitionFromDevPath Loop %x\n", Status
));
546 // Stall 100ms if connection failed to ensure USB stack is ready
551 } while(EFI_ERROR(Status
));
554 // Find a qualified Simple File System
556 if (!EFI_ERROR(Status
)) {
563 // Return the OptionNumber of the boot option where EFI system partition is got from
565 if (*LoadOptionNumber
== NULL
) {
566 *LoadOptionNumber
= AllocateCopyPool (sizeof(UINT16
), (UINT16
*) &BootOptionBuf
[Index
].OptionNumber
);
570 // No qualified EFI system partition found
572 if (*FsHandle
== NULL
) {
573 Status
= EFI_NOT_FOUND
;
577 CHAR16
*DevicePathStr2
;
578 if (*FsHandle
!= NULL
) {
579 DevicePathStr2
= ConvertDevicePathToText(CurFullPath
, TRUE
, TRUE
);
580 if (DevicePathStr2
!= NULL
){
581 DEBUG((DEBUG_INFO
, "Found Active EFI System Partion on %s\n", DevicePathStr2
));
582 FreePool(DevicePathStr2
);
585 DEBUG((DEBUG_INFO
, "Failed to found Active EFI System Partion\n"));
589 if (CurFullPath
!= NULL
) {
590 FreePool(CurFullPath
);
594 // Free BootOption Buffer
596 for (Index
= 0; Index
< BootOptionNum
; Index
++) {
597 if (BootOptionBuf
[Index
].Description
!= NULL
) {
598 FreePool(BootOptionBuf
[Index
].Description
);
601 if (BootOptionBuf
[Index
].FilePath
!= NULL
) {
602 FreePool(BootOptionBuf
[Index
].FilePath
);
605 if (BootOptionBuf
[Index
].OptionalData
!= NULL
) {
606 FreePool(BootOptionBuf
[Index
].OptionalData
);
610 FreePool(BootOptionBuf
);
617 This routine is called to get all file infos with in a given dir & with given file attribute, the file info is listed in
618 alphabetical order described in UEFI spec.
620 @param[in] Dir Directory file handler
621 @param[in] FileAttr Attribute of file to be red from directory
622 @param[out] FileInfoList File images info list red from directory
623 @param[out] FileNum File images number red from directory
625 @retval EFI_SUCCESS File FileInfo list in the given
629 GetFileInfoListInAlphabetFromDir(
630 IN EFI_FILE_HANDLE Dir
,
632 OUT LIST_ENTRY
*FileInfoList
,
637 FILE_INFO_ENTRY
*NewFileInfoEntry
;
638 FILE_INFO_ENTRY
*TempFileInfoEntry
;
639 EFI_FILE_INFO
*FileInfo
;
641 CHAR16
*ListedFileName
;
642 CHAR16
*NewFileNameExtension
;
643 CHAR16
*ListedFileNameExtension
;
644 CHAR16
*TempNewSubStr
;
645 CHAR16
*TempListedSubStr
;
652 UINTN ListedSubStrLen
;
653 INTN SubStrCmpResult
;
655 Status
= EFI_SUCCESS
;
657 ListedFileName
= NULL
;
658 NewFileNameExtension
= NULL
;
659 ListedFileNameExtension
= NULL
;
660 TempNewSubStr
= NULL
;
661 TempListedSubStr
= NULL
;
665 InitializeListHead(FileInfoList
);
667 TempNewSubStr
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
668 TempListedSubStr
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
670 if (TempNewSubStr
== NULL
|| TempListedSubStr
== NULL
) {
671 Status
= EFI_OUT_OF_RESOURCES
;
675 for ( Status
= FileHandleFindFirstFile(Dir
, &FileInfo
)
676 ; !EFI_ERROR(Status
) && !NoFile
677 ; Status
= FileHandleFindNextFile(Dir
, FileInfo
, &NoFile
)
681 // Skip file with mismatching File attribute
683 if ((FileInfo
->Attribute
& (FileAttr
)) == 0) {
687 NewFileInfoEntry
= NULL
;
688 NewFileInfoEntry
= (FILE_INFO_ENTRY
*)AllocateZeroPool(sizeof(FILE_INFO_ENTRY
));
689 if (NewFileInfoEntry
== NULL
) {
690 Status
= EFI_OUT_OF_RESOURCES
;
693 NewFileInfoEntry
->Signature
= FILE_INFO_SIGNATURE
;
694 NewFileInfoEntry
->FileInfo
= AllocateCopyPool((UINTN
) FileInfo
->Size
, FileInfo
);
695 if (NewFileInfoEntry
->FileInfo
== NULL
) {
696 FreePool(NewFileInfoEntry
);
697 Status
= EFI_OUT_OF_RESOURCES
;
701 NewFileInfoEntry
->FileNameFirstPart
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
702 if (NewFileInfoEntry
->FileNameFirstPart
== NULL
) {
703 FreePool(NewFileInfoEntry
->FileInfo
);
704 FreePool(NewFileInfoEntry
);
705 Status
= EFI_OUT_OF_RESOURCES
;
708 NewFileInfoEntry
->FileNameSecondPart
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
709 if (NewFileInfoEntry
->FileNameSecondPart
== NULL
) {
710 FreePool(NewFileInfoEntry
->FileInfo
);
711 FreePool(NewFileInfoEntry
->FileNameFirstPart
);
712 FreePool(NewFileInfoEntry
);
713 Status
= EFI_OUT_OF_RESOURCES
;
718 // Splitter the whole New file name into 2 parts between the last period L'.' into NewFileName NewFileExtension
719 // If no period in the whole file name. NewFileExtension is set to L'\0'
721 NewFileName
= NewFileInfoEntry
->FileNameFirstPart
;
722 NewFileNameExtension
= NewFileInfoEntry
->FileNameSecondPart
;
723 SplitFileNameExtension(FileInfo
->FileName
, NewFileName
, NewFileNameExtension
);
724 UpperCaseString(NewFileName
);
725 UpperCaseString(NewFileNameExtension
);
728 // Insert capsule file in alphabetical ordered list
730 for (Link
= FileInfoList
->ForwardLink
; Link
!= FileInfoList
; Link
= Link
->ForwardLink
) {
732 // Get the FileInfo from the link list
734 TempFileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
735 ListedFileName
= TempFileInfoEntry
->FileNameFirstPart
;
736 ListedFileNameExtension
= TempFileInfoEntry
->FileNameSecondPart
;
739 // Follow rule in UEFI spec 8.5.5 to compare file name
745 // First compare each substrings in NewFileName & ListedFileName between periods
747 GetSubStringBeforePeriod(&NewFileName
[IndexNew
], TempNewSubStr
, &NewSubStrLen
);
748 GetSubStringBeforePeriod(&ListedFileName
[IndexListed
], TempListedSubStr
, &ListedSubStrLen
);
749 if (NewSubStrLen
> ListedSubStrLen
) {
751 // Substr in NewFileName is longer. Pad tail with SPACE
753 PadStrInTail(TempListedSubStr
, NewSubStrLen
- ListedSubStrLen
, L
' ');
754 } else if (NewSubStrLen
< ListedSubStrLen
){
756 // Substr in ListedFileName is longer. Pad tail with SPACE
758 PadStrInTail(TempNewSubStr
, ListedSubStrLen
- NewSubStrLen
, L
' ');
761 SubStrCmpResult
= StrnCmp(TempNewSubStr
, TempListedSubStr
, MAX_FILE_NAME_LEN
);
762 if (SubStrCmpResult
!= 0) {
767 // Move to skip this substring
769 IndexNew
+= NewSubStrLen
;
770 IndexListed
+= ListedSubStrLen
;
772 // Reach File First Name end
774 if (NewFileName
[IndexNew
] == L
'\0' || ListedFileName
[IndexListed
] == L
'\0') {
779 // Skip the period L'.'
785 if (SubStrCmpResult
< 0) {
787 // NewFileName is smaller. Find the right place to insert New file
790 } else if (SubStrCmpResult
== 0) {
792 // 2 cases whole NewFileName is smaller than ListedFileName
793 // 1. if NewFileName == ListedFileName. Continue to compare FileNameExtension
794 // 2. if NewFileName is shorter than ListedFileName
796 if (NewFileName
[IndexNew
] == L
'\0') {
797 if (ListedFileName
[IndexListed
] != L
'\0' || (StrnCmp(NewFileNameExtension
, ListedFileNameExtension
, MAX_FILE_NAME_LEN
) < 0)) {
804 // Other case, ListedFileName is smaller. Continue to compare the next file in the list
809 // If Find an entry in the list whose name is bigger than new FileInfo in alphabet order
810 // Insert it before this entry
812 // Insert at the tail of this list (Link = FileInfoList)
814 InsertTailList(Link
, &NewFileInfoEntry
->Link
);
819 *FileNum
= FileCount
;
823 if (TempNewSubStr
!= NULL
) {
824 FreePool(TempNewSubStr
);
827 if (TempListedSubStr
!= NULL
) {
828 FreePool(TempListedSubStr
);
831 if (EFI_ERROR(Status
)) {
832 while(!IsListEmpty(FileInfoList
)) {
833 Link
= FileInfoList
->ForwardLink
;
834 RemoveEntryList(Link
);
836 TempFileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
838 FreePool(TempFileInfoEntry
->FileInfo
);
839 FreePool(TempFileInfoEntry
->FileNameFirstPart
);
840 FreePool(TempFileInfoEntry
->FileNameSecondPart
);
841 FreePool(TempFileInfoEntry
);
851 This routine is called to get all qualified image from file from an given directory
852 in alphabetic order. All the file image is copied to allocated boottime memory.
853 Caller should free these memory
855 @param[in] Dir Directory file handler
856 @param[in] FileAttr Attribute of file to be red from directory
857 @param[out] FilePtr File images Info buffer red from directory
858 @param[out] FileNum File images number red from directory
860 @retval EFI_SUCCESS Succeed to get all capsules in alphabetic order.
864 GetFileImageInAlphabetFromDir(
865 IN EFI_FILE_HANDLE Dir
,
867 OUT IMAGE_INFO
**FilePtr
,
873 EFI_FILE_HANDLE FileHandle
;
874 FILE_INFO_ENTRY
*FileInfoEntry
;
875 EFI_FILE_INFO
*FileInfo
;
877 IMAGE_INFO
*TempFilePtrBuf
;
879 LIST_ENTRY FileInfoList
;
883 TempFilePtrBuf
= NULL
;
887 // Get file list in Dir in alphabetical order
889 Status
= GetFileInfoListInAlphabetFromDir(
895 if (EFI_ERROR(Status
)) {
896 DEBUG ((DEBUG_ERROR
, "GetFileInfoListInAlphabetFromDir Failed!\n"));
900 if (FileCount
== 0) {
901 DEBUG ((DEBUG_ERROR
, "No file found in Dir!\n"));
902 Status
= EFI_NOT_FOUND
;
906 TempFilePtrBuf
= (IMAGE_INFO
*)AllocateZeroPool(sizeof(IMAGE_INFO
) * FileCount
);
907 if (TempFilePtrBuf
== NULL
) {
908 Status
= EFI_OUT_OF_RESOURCES
;
913 // Read all files from FileInfoList to BS memory
916 for (Link
= FileInfoList
.ForwardLink
; Link
!= &FileInfoList
; Link
= Link
->ForwardLink
) {
918 // Get FileInfo from the link list
920 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
921 FileInfo
= FileInfoEntry
->FileInfo
;
930 if (EFI_ERROR(Status
)){
934 Size
= (UINTN
)FileInfo
->FileSize
;
935 TempFilePtrBuf
[FileCount
].ImageAddress
= AllocateZeroPool(Size
);
936 if (TempFilePtrBuf
[FileCount
].ImageAddress
== NULL
) {
937 DEBUG((DEBUG_ERROR
, "Fail to allocate memory for capsule. Stop processing the rest.\n"));
941 Status
= FileHandle
->Read(
944 TempFilePtrBuf
[FileCount
].ImageAddress
947 FileHandle
->Close(FileHandle
);
950 // Skip read error file
952 if (EFI_ERROR(Status
) || Size
!= (UINTN
)FileInfo
->FileSize
) {
954 // Remove this error file info accordingly
955 // & move Link to BackLink
957 Link
= RemoveEntryList(Link
);
958 Link
= Link
->BackLink
;
960 FreePool(FileInfoEntry
->FileInfo
);
961 FreePool(FileInfoEntry
->FileNameFirstPart
);
962 FreePool(FileInfoEntry
->FileNameSecondPart
);
963 FreePool(FileInfoEntry
);
965 FreePool(TempFilePtrBuf
[FileCount
].ImageAddress
);
966 TempFilePtrBuf
[FileCount
].ImageAddress
= NULL
;
967 TempFilePtrBuf
[FileCount
].FileInfo
= NULL
;
971 TempFilePtrBuf
[FileCount
].FileInfo
= FileInfo
;
976 for (Link
= FileInfoList
.ForwardLink
; Link
!= &FileInfoList
; Link
= Link
->ForwardLink
) {
977 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
978 FileInfo
= FileInfoEntry
->FileInfo
;
979 DEBUG((DEBUG_INFO
, "Successfully read capsule file %s from disk.\n", FileInfo
->FileName
));
985 *FilePtr
= TempFilePtrBuf
;
986 *FileNum
= FileCount
;
989 // FileInfo will be freed by Calller
991 while(!IsListEmpty(&FileInfoList
)) {
992 Link
= FileInfoList
.ForwardLink
;
993 RemoveEntryList(Link
);
995 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
997 FreePool(FileInfoEntry
->FileNameFirstPart
);
998 FreePool(FileInfoEntry
->FileNameSecondPart
);
999 FreePool(FileInfoEntry
);
1006 This routine is called to remove all qualified image from file from an given directory.
1008 @param[in] Dir Directory file handler
1009 @param[in] FileAttr Attribute of files to be deleted
1011 @retval EFI_SUCCESS Succeed to remove all files from an given directory.
1016 IN EFI_FILE_HANDLE Dir
,
1022 LIST_ENTRY FileInfoList
;
1023 EFI_FILE_HANDLE FileHandle
;
1024 FILE_INFO_ENTRY
*FileInfoEntry
;
1025 EFI_FILE_INFO
*FileInfo
;
1031 // Get file list in Dir in alphabetical order
1033 Status
= GetFileInfoListInAlphabetFromDir(
1039 if (EFI_ERROR(Status
)) {
1040 DEBUG ((DEBUG_ERROR
, "GetFileInfoListInAlphabetFromDir Failed!\n"));
1044 if (FileCount
== 0) {
1045 DEBUG ((DEBUG_ERROR
, "No file found in Dir!\n"));
1046 Status
= EFI_NOT_FOUND
;
1051 // Delete all files with given attribute in Dir
1053 for (Link
= FileInfoList
.ForwardLink
; Link
!= &(FileInfoList
); Link
= Link
->ForwardLink
) {
1055 // Get FileInfo from the link list
1057 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
1058 FileInfo
= FileInfoEntry
->FileInfo
;
1064 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
,
1067 if (EFI_ERROR(Status
)){
1071 Status
= FileHandle
->Delete(FileHandle
);
1076 while(!IsListEmpty(&FileInfoList
)) {
1077 Link
= FileInfoList
.ForwardLink
;
1078 RemoveEntryList(Link
);
1080 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
1082 FreePool(FileInfoEntry
->FileInfo
);
1083 FreePool(FileInfoEntry
);
1090 This routine is called to get all caspules from file. The capsule file image is
1091 copied to BS memory. Caller is responsible to free them.
1093 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1094 devices like USB can get enumerated.
1095 @param[out] CapsulePtr Copied Capsule file Image Info buffer
1096 @param[out] CapsuleNum CapsuleNumber
1097 @param[out] FsHandle File system handle
1098 @param[out] LoadOptionNumber OptionNumber of boot option
1100 @retval EFI_SUCCESS Succeed to get all capsules.
1104 GetAllCapsuleOnDisk(
1106 OUT IMAGE_INFO
**CapsulePtr
,
1107 OUT UINTN
*CapsuleNum
,
1108 OUT EFI_HANDLE
*FsHandle
,
1109 OUT UINT16
*LoadOptionNumber
1113 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
1114 EFI_FILE_HANDLE RootDir
;
1115 EFI_FILE_HANDLE FileDir
;
1116 UINT16
*TempOptionNumber
;
1121 TempOptionNumber
= NULL
;
1124 Status
= GetEfiSysPartitionFromActiveBootOption(MaxRetry
, &TempOptionNumber
, FsHandle
);
1125 if (EFI_ERROR(Status
)) {
1129 Status
= gBS
->HandleProtocol(*FsHandle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
1130 if (EFI_ERROR(Status
)) {
1134 Status
= Fs
->OpenVolume(Fs
, &RootDir
);
1135 if (EFI_ERROR(Status
)) {
1139 Status
= RootDir
->Open(
1142 EFI_CAPSULE_FILE_DIRECTORY
,
1146 if (EFI_ERROR(Status
)) {
1147 DEBUG((DEBUG_ERROR
, "CodLibGetAllCapsuleOnDisk fail to open RootDir!\n"));
1152 // Only Load files with EFI_FILE_SYSTEM or EFI_FILE_ARCHIVE attribute
1153 // ignore EFI_FILE_READ_ONLY, EFI_FILE_HIDDEN, EFI_FILE_RESERVED, EFI_FILE_DIRECTORY
1155 Status
= GetFileImageInAlphabetFromDir(
1157 EFI_FILE_SYSTEM
| EFI_FILE_ARCHIVE
,
1161 DEBUG((DEBUG_INFO
, "GetFileImageInAlphabetFromDir status %x\n", Status
));
1164 // Always remove file to avoid deadloop in capsule process
1166 Status
= RemoveFileFromDir(FileDir
, EFI_FILE_SYSTEM
| EFI_FILE_ARCHIVE
);
1167 DEBUG((DEBUG_INFO
, "RemoveFileFromDir status %x\n", Status
));
1169 if (LoadOptionNumber
!= NULL
) {
1170 *LoadOptionNumber
= *TempOptionNumber
;
1175 if (FileDir
!= NULL
) {
1176 FileDir
->Close (FileDir
);
1179 if (RootDir
!= NULL
) {
1180 RootDir
->Close (RootDir
);
1187 Build Gather list for a list of capsule images.
1189 @param[in] CapsuleBuffer An array of pointer to capsule images
1190 @param[in] CapsuleSize An array of UINTN to capsule images size
1191 @param[in] CapsuleNum The count of capsule images
1192 @param[out] BlockDescriptors The block descriptors for the capsule images
1194 @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.
1199 IN VOID
**CapsuleBuffer
,
1200 IN UINTN
*CapsuleSize
,
1201 IN UINTN CapsuleNum
,
1202 OUT EFI_CAPSULE_BLOCK_DESCRIPTOR
**BlockDescriptors
1206 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptors1
;
1207 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptorPre
;
1208 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptorsHeader
;
1211 BlockDescriptors1
= NULL
;
1212 BlockDescriptorPre
= NULL
;
1213 BlockDescriptorsHeader
= NULL
;
1215 for (Index
= 0; Index
< CapsuleNum
; Index
++) {
1217 // Allocate memory for the descriptors.
1219 BlockDescriptors1
= AllocateZeroPool (2 * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR
));
1220 if (BlockDescriptors1
== NULL
) {
1221 DEBUG ((DEBUG_ERROR
, "BuildGatherList: failed to allocate memory for descriptors\n"));
1222 Status
= EFI_OUT_OF_RESOURCES
;
1225 DEBUG ((DEBUG_INFO
, "BuildGatherList: creating capsule descriptors at 0x%X\n", (UINTN
) BlockDescriptors1
));
1229 // Record descirptor header
1232 BlockDescriptorsHeader
= BlockDescriptors1
;
1235 if (BlockDescriptorPre
!= NULL
) {
1236 BlockDescriptorPre
->Union
.ContinuationPointer
= (UINTN
) BlockDescriptors1
;
1237 BlockDescriptorPre
->Length
= 0;
1240 BlockDescriptors1
->Union
.DataBlock
= (UINTN
) CapsuleBuffer
[Index
];
1241 BlockDescriptors1
->Length
= CapsuleSize
[Index
];
1243 BlockDescriptorPre
= BlockDescriptors1
+ 1;
1244 BlockDescriptors1
= NULL
;
1250 if (BlockDescriptorPre
!= NULL
) {
1251 BlockDescriptorPre
->Union
.ContinuationPointer
= (UINTN
)NULL
;
1252 BlockDescriptorPre
->Length
= 0;
1253 *BlockDescriptors
= BlockDescriptorsHeader
;
1259 if (BlockDescriptors1
!= NULL
) {
1260 FreePool (BlockDescriptors1
);
1267 This routine is called to check if CapsuleOnDisk flag in OsIndications Variable
1270 @retval TRUE Flag is enabled
1271 @retval FALSE Flag is not enabled
1276 CoDCheckCapsuleOnDiskFlag(
1281 UINT64 OsIndication
;
1285 // Check File Capsule Delivery Supported Flag in OsIndication variable
1288 DataSize
= sizeof(UINT64
);
1289 Status
= gRT
->GetVariable (
1290 EFI_OS_INDICATIONS_VARIABLE_NAME
,
1291 &gEfiGlobalVariableGuid
,
1296 if (!EFI_ERROR(Status
) &&
1297 (OsIndication
& EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
) != 0) {
1306 This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable.
1308 @retval EFI_SUCCESS All Capsule On Disk flags are cleared
1313 CoDClearCapsuleOnDiskFlag(
1318 UINT64 OsIndication
;
1322 // Reset File Capsule Delivery Supported Flag in OsIndication variable
1325 DataSize
= sizeof(UINT64
);
1326 Status
= gRT
->GetVariable (
1327 EFI_OS_INDICATIONS_VARIABLE_NAME
,
1328 &gEfiGlobalVariableGuid
,
1333 if (EFI_ERROR(Status
) ||
1334 (OsIndication
& EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
) == 0) {
1338 OsIndication
&= ~((UINT64
)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
);
1339 Status
= gRT
->SetVariable (
1340 EFI_OS_INDICATIONS_VARIABLE_NAME
,
1341 &gEfiGlobalVariableGuid
,
1342 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
1346 ASSERT(!EFI_ERROR(Status
));
1349 // Delete BootNext variable. Capsule Process may reset system, so can't rely on Bds to clear this variable
1351 Status
= gRT
->SetVariable (
1352 EFI_BOOT_NEXT_VARIABLE_NAME
,
1353 &gEfiGlobalVariableGuid
,
1358 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_NOT_FOUND
);
1364 This routine is called to clear CapsuleOnDisk Relocation Info variable.
1365 Total Capsule On Disk length is recorded in this variable
1367 @retval EFI_SUCCESS Capsule On Disk flags are cleared
1371 CoDClearCapsuleRelocationInfo(
1375 return gRT
->SetVariable (
1376 COD_RELOCATION_INFO_VAR_NAME
,
1377 &gEfiCapsuleVendorGuid
,
1385 Relocate Capsule on Disk from EFI system partition to a platform-specific NV storage device
1386 with BlockIo protocol. Relocation device path, identified by PcdCodRelocationDevPath, must
1387 be a full device path.
1388 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1389 Function will stall 100ms between each retry.
1392 Content corruption. Block IO write directly touches low level write. Orignal partitions, file systems
1393 of the relocation device will be corrupted.
1395 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1396 devices like USB can get enumerated.
1398 @retval EFI_SUCCESS Capsule on Disk images are sucessfully relocated to the platform-specific device.
1402 RelocateCapsuleToDisk(
1407 UINTN CapsuleOnDiskNum
;
1410 UINT64 TotalImageSize
;
1411 UINT64 TotalImageNameSize
;
1412 IMAGE_INFO
*CapsuleOnDiskBuf
;
1414 EFI_HANDLE TempHandle
;
1415 EFI_HANDLE
*HandleBuffer
;
1416 UINTN NumberOfHandles
;
1417 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
1418 UINT8
*CapsuleDataBuf
;
1420 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
1421 EFI_FILE_HANDLE RootDir
;
1422 EFI_FILE_HANDLE TempCodFile
;
1423 UINT64 TempCodFileSize
;
1424 EFI_DEVICE_PATH
*TempDevicePath
;
1425 BOOLEAN RelocationInfo
;
1426 UINT16 LoadOptionNumber
;
1427 EFI_CAPSULE_HEADER FileNameCapsuleHeader
;
1431 HandleBuffer
= NULL
;
1432 CapsuleDataBuf
= NULL
;
1433 CapsuleOnDiskBuf
= NULL
;
1434 NumberOfHandles
= 0;
1436 DEBUG ((DEBUG_INFO
, "CapsuleOnDisk RelocateCapsule Enter\n"));
1439 // 1. Load all Capsule On Disks in to memory
1441 Status
= GetAllCapsuleOnDisk(MaxRetry
, &CapsuleOnDiskBuf
, &CapsuleOnDiskNum
, &Handle
, &LoadOptionNumber
);
1442 if (EFI_ERROR(Status
) || CapsuleOnDiskNum
== 0) {
1443 DEBUG ((DEBUG_INFO
, "RelocateCapsule: GetAllCapsuleOnDisk Status - 0x%x\n", Status
));
1444 return EFI_NOT_FOUND
;
1448 // 2. Connect platform special device path as relocation device.
1449 // If no platform special device path specified or the device path is invalid, use the EFI system partition where
1450 // stores the capsules as relocation device.
1452 if (IsDevicePathValid ((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
), PcdGetSize(PcdCodRelocationDevPath
))) {
1453 Status
= EfiBootManagerConnectDevicePath ((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
), &TempHandle
);
1454 if (EFI_ERROR(Status
)) {
1455 DEBUG ((DEBUG_ERROR
, "RelocateCapsule: EfiBootManagerConnectDevicePath Status - 0x%x\n", Status
));
1460 // Connect all the child handle. Partition & FAT drivers are allowed in this case
1462 gBS
->ConnectController (TempHandle
, NULL
, NULL
, TRUE
);
1463 Status
= gBS
->LocateHandleBuffer(
1465 &gEfiSimpleFileSystemProtocolGuid
,
1470 if (EFI_ERROR(Status
)) {
1471 DEBUG ((DEBUG_ERROR
, "RelocateCapsule: LocateHandleBuffer Status - 0x%x\n", Status
));
1476 // Find first Simple File System Handle which can match PcdCodRelocationDevPath
1478 for (Index
= 0; Index
< NumberOfHandles
; Index
++) {
1479 Status
= gBS
->HandleProtocol(HandleBuffer
[Index
], &gEfiDevicePathProtocolGuid
, (VOID
**)&TempDevicePath
);
1480 if (EFI_ERROR(Status
)) {
1484 DataSize
= GetDevicePathSize((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
)) - sizeof(EFI_DEVICE_PATH
);
1485 if (0 == CompareMem((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
), TempDevicePath
, DataSize
)) {
1486 Handle
= HandleBuffer
[Index
];
1491 FreePool(HandleBuffer
);
1493 if (Index
== NumberOfHandles
) {
1494 DEBUG ((DEBUG_ERROR
, "RelocateCapsule: No simple file system protocol found.\n"));
1495 Status
= EFI_NOT_FOUND
;
1499 Status
= gBS
->HandleProtocol(Handle
, &gEfiBlockIoProtocolGuid
, (VOID
**)&BlockIo
);
1500 if (EFI_ERROR(Status
) || BlockIo
->Media
->ReadOnly
) {
1501 DEBUG((DEBUG_ERROR
, "Fail to find Capsule on Disk relocation BlockIo device or device is ReadOnly!\n"));
1505 Status
= gBS
->HandleProtocol(Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
1506 if (EFI_ERROR(Status
)) {
1511 // Check if device used to relocate Capsule On Disk is big enough
1514 TotalImageNameSize
= 0;
1515 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1519 if (MAX_ADDRESS
- (UINTN
)TotalImageSize
<= CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
) {
1520 Status
= EFI_INVALID_PARAMETER
;
1524 if (MAX_ADDRESS
- (UINTN
)TotalImageNameSize
<= StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
)) {
1525 Status
= EFI_INVALID_PARAMETER
;
1529 TotalImageSize
+= CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
;
1530 TotalImageNameSize
+= StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1531 DEBUG((DEBUG_INFO
, "RelocateCapsule: %x Size %x\n",CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
, CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
));
1534 DEBUG((DEBUG_INFO
, "RelocateCapsule: TotalImageSize %x\n", TotalImageSize
));
1535 DEBUG((DEBUG_INFO
, "RelocateCapsule: TotalImageNameSize %x\n", TotalImageNameSize
));
1537 if (MAX_ADDRESS
- (UINTN
)TotalImageNameSize
<= sizeof(UINT64
) * 2 ||
1538 MAX_ADDRESS
- (UINTN
)TotalImageSize
<= (UINTN
)TotalImageNameSize
+ sizeof(UINT64
) * 2) {
1539 Status
= EFI_INVALID_PARAMETER
;
1543 TempCodFileSize
= sizeof(UINT64
) + TotalImageSize
+ sizeof(EFI_CAPSULE_HEADER
) + TotalImageNameSize
;
1546 // Check if CapsuleTotalSize. There could be reminder, so use LastBlock number directly
1548 if (DivU64x32(TempCodFileSize
, BlockIo
->Media
->BlockSize
) > BlockIo
->Media
->LastBlock
) {
1549 DEBUG((DEBUG_ERROR
, "RelocateCapsule: Relocation device isn't big enough to hold all Capsule on Disk!\n"));
1550 DEBUG((DEBUG_ERROR
, "TotalImageSize = %x\n", TotalImageSize
));
1551 DEBUG((DEBUG_ERROR
, "TotalImageNameSize = %x\n", TotalImageNameSize
));
1552 DEBUG((DEBUG_ERROR
, "RelocationDev BlockSize = %x LastBlock = %x\n", BlockIo
->Media
->BlockSize
, BlockIo
->Media
->LastBlock
));
1553 Status
= EFI_OUT_OF_RESOURCES
;
1557 CapsuleDataBuf
= AllocatePool((UINTN
) TempCodFileSize
);
1558 if (CapsuleDataBuf
== NULL
) {
1559 Status
= EFI_OUT_OF_RESOURCES
;
1564 // First UINT64 reserved for total image size, including capsule name capsule.
1566 *(UINT64
*) CapsuleDataBuf
= TotalImageSize
+ sizeof(EFI_CAPSULE_HEADER
) + TotalImageNameSize
;
1569 // Line up all the Capsule on Disk and write to relocation disk at one time. It could save some time in disk write
1571 for (Index
= 0, CapsulePtr
= CapsuleDataBuf
+ sizeof(UINT64
); Index
< CapsuleOnDiskNum
; Index
++) {
1572 CopyMem(CapsulePtr
, CapsuleOnDiskBuf
[Index
].ImageAddress
, (UINTN
) CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
);
1573 CapsulePtr
+= CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
;
1577 // Line the capsule header for capsule name capsule.
1579 CopyGuid(&FileNameCapsuleHeader
.CapsuleGuid
, &gEdkiiCapsuleOnDiskNameGuid
);
1580 FileNameCapsuleHeader
.CapsuleImageSize
= (UINT32
) TotalImageNameSize
+ sizeof(EFI_CAPSULE_HEADER
);
1581 FileNameCapsuleHeader
.Flags
= CAPSULE_FLAGS_PERSIST_ACROSS_RESET
;
1582 FileNameCapsuleHeader
.HeaderSize
= sizeof(EFI_CAPSULE_HEADER
);
1583 CopyMem(CapsulePtr
, &FileNameCapsuleHeader
, FileNameCapsuleHeader
.HeaderSize
);
1584 CapsulePtr
+= FileNameCapsuleHeader
.HeaderSize
;
1587 // Line up all the Capsule file names.
1589 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1590 CopyMem(CapsulePtr
, CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
, StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
));
1591 CapsulePtr
+= StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1595 // 5. Flash all Capsules on Disk to TempCoD.tmp under RootDir
1597 Status
= Fs
->OpenVolume(Fs
, &RootDir
);
1598 if (EFI_ERROR(Status
)) {
1599 DEBUG((DEBUG_ERROR
, "RelocateCapsule: OpenVolume error. %x\n", Status
));
1603 Status
= RootDir
->Open(
1606 (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
),
1607 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
,
1610 if (!EFI_ERROR(Status
)) {
1612 // Error handling code to prevent malicious code to hold this file to block capsule on disk
1614 TempCodFile
->Delete(TempCodFile
);
1616 Status
= RootDir
->Open(
1619 (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
),
1620 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
,
1623 if (EFI_ERROR(Status
)) {
1624 DEBUG((DEBUG_ERROR
, "RelocateCapsule: Open TemCoD.tmp error. %x\n", Status
));
1629 // Always write at the begining of TempCap file
1631 DataSize
= (UINTN
) TempCodFileSize
;
1632 Status
= TempCodFile
->Write(
1637 if (EFI_ERROR(Status
)) {
1638 DEBUG((DEBUG_ERROR
, "RelocateCapsule: Write TemCoD.tmp error. %x\n", Status
));
1642 if (DataSize
!= TempCodFileSize
) {
1643 Status
= EFI_DEVICE_ERROR
;
1648 // Save Capsule On Disk relocation info to "CodRelocationInfo" Var
1649 // It is used in next reboot by TCB
1651 RelocationInfo
= TRUE
;
1652 Status
= gRT
->SetVariable(
1653 COD_RELOCATION_INFO_VAR_NAME
,
1654 &gEfiCapsuleVendorGuid
,
1655 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
1660 // Save the LoadOptionNumber of the boot option, where the capsule is relocated,
1661 // into "CodRelocationLoadOption" var. It is used in next reboot after capsule is
1662 // updated out of TCB to remove the TempCoDFile.
1664 Status
= gRT
->SetVariable(
1665 COD_RELOCATION_LOAD_OPTION_VAR_NAME
,
1666 &gEfiCapsuleVendorGuid
,
1667 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
1674 if (CapsuleDataBuf
!= NULL
) {
1675 FreePool(CapsuleDataBuf
);
1678 if (CapsuleOnDiskBuf
!= NULL
) {
1680 // Free resources allocated by CodLibGetAllCapsuleOnDisk
1682 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++ ) {
1683 FreePool(CapsuleOnDiskBuf
[Index
].ImageAddress
);
1684 FreePool(CapsuleOnDiskBuf
[Index
].FileInfo
);
1686 FreePool(CapsuleOnDiskBuf
);
1689 if (TempCodFile
!= NULL
) {
1690 if (EFI_ERROR(Status
)) {
1691 TempCodFile
->Delete (TempCodFile
);
1693 TempCodFile
->Close (TempCodFile
);
1697 if (RootDir
!= NULL
) {
1698 RootDir
->Close (RootDir
);
1705 For the platforms that support Capsule In Ram, reuse the Capsule In Ram to deliver capsule.
1706 Relocate Capsule On Disk to memory and call UpdateCapsule().
1707 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1708 Function will stall 100ms between each retry.
1710 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1711 devices like USB can get enumerated.
1713 @retval EFI_SUCCESS Deliver capsule through Capsule In Ram successfully.
1717 RelocateCapsuleToRam (
1722 UINTN CapsuleOnDiskNum
;
1723 IMAGE_INFO
*CapsuleOnDiskBuf
;
1725 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptors
;
1726 VOID
**CapsuleBuffer
;
1728 EFI_CAPSULE_HEADER
*FileNameCapsule
;
1732 UINTN TotalStringSize
;
1734 CapsuleOnDiskBuf
= NULL
;
1735 BlockDescriptors
= NULL
;
1736 CapsuleBuffer
= NULL
;
1738 FileNameCapsule
= NULL
;
1739 TotalStringSize
= 0;
1742 // 1. Load all Capsule On Disks into memory
1744 Status
= GetAllCapsuleOnDisk (MaxRetry
, &CapsuleOnDiskBuf
, &CapsuleOnDiskNum
, &Handle
, NULL
);
1745 if (EFI_ERROR (Status
) || CapsuleOnDiskNum
== 0) {
1746 DEBUG ((DEBUG_ERROR
, "GetAllCapsuleOnDisk Status - 0x%x\n", Status
));
1747 return EFI_NOT_FOUND
;
1751 // 2. Add a capsule for Capsule file name strings
1753 CapsuleBuffer
= AllocateZeroPool ((CapsuleOnDiskNum
+ 1) * sizeof (VOID
*));
1754 if (CapsuleBuffer
== NULL
) {
1755 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory for capsules.\n"));
1756 return EFI_OUT_OF_RESOURCES
;
1759 CapsuleSize
= AllocateZeroPool ((CapsuleOnDiskNum
+ 1) * sizeof (UINTN
));
1760 if (CapsuleSize
== NULL
) {
1761 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory for capsules.\n"));
1762 FreePool (CapsuleBuffer
);
1763 return EFI_OUT_OF_RESOURCES
;
1766 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1767 CapsuleBuffer
[Index
] = (VOID
*)(UINTN
) CapsuleOnDiskBuf
[Index
].ImageAddress
;
1768 CapsuleSize
[Index
] = (UINTN
) CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
;
1769 TotalStringSize
+= StrSize (CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1772 FileNameCapsule
= AllocateZeroPool (sizeof (EFI_CAPSULE_HEADER
) + TotalStringSize
);
1773 if (FileNameCapsule
== NULL
) {
1774 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory for name capsule.\n"));
1775 FreePool (CapsuleBuffer
);
1776 FreePool (CapsuleSize
);
1777 return EFI_OUT_OF_RESOURCES
;
1780 FileNameCapsule
->CapsuleImageSize
= (UINT32
) (sizeof (EFI_CAPSULE_HEADER
) + TotalStringSize
);
1781 FileNameCapsule
->Flags
= CAPSULE_FLAGS_PERSIST_ACROSS_RESET
;
1782 FileNameCapsule
->HeaderSize
= sizeof (EFI_CAPSULE_HEADER
);
1783 CopyGuid (&(FileNameCapsule
->CapsuleGuid
), &gEdkiiCapsuleOnDiskNameGuid
);
1785 StringBuf
= (UINT8
*)FileNameCapsule
+ FileNameCapsule
->HeaderSize
;
1786 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1787 StringSize
= StrSize (CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1788 CopyMem (StringBuf
, CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
, StringSize
);
1789 StringBuf
+= StringSize
;
1792 CapsuleBuffer
[CapsuleOnDiskNum
] = FileNameCapsule
;
1793 CapsuleSize
[CapsuleOnDiskNum
] = TotalStringSize
+ sizeof (EFI_CAPSULE_HEADER
);
1796 // 3. Build Gather list for the capsules
1798 Status
= BuildGatherList (CapsuleBuffer
, CapsuleSize
, CapsuleOnDiskNum
+ 1, &BlockDescriptors
);
1799 if (EFI_ERROR (Status
) || BlockDescriptors
== NULL
) {
1800 FreePool (CapsuleBuffer
);
1801 FreePool (CapsuleSize
);
1802 FreePool (FileNameCapsule
);
1803 return EFI_OUT_OF_RESOURCES
;
1807 // 4. Call UpdateCapsule() service
1809 Status
= gRT
->UpdateCapsule((EFI_CAPSULE_HEADER
**) CapsuleBuffer
, CapsuleOnDiskNum
+ 1, (UINTN
) BlockDescriptors
);
1815 Relocate Capsule on Disk from EFI system partition.
1817 Two solution to deliver Capsule On Disk:
1818 Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().
1819 Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage
1820 device with BlockIo protocol.
1822 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1823 Function will stall 100ms between each retry.
1826 Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.
1827 Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file
1828 systems of the relocation device will be corrupted.
1830 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1831 devices like USB can get enumerated. Input 0 means no retry.
1833 @retval EFI_SUCCESS Capsule on Disk images are successfully relocated.
1842 if (!PcdGetBool (PcdCapsuleOnDiskSupport
)) {
1843 return EFI_UNSUPPORTED
;
1847 // Clear CapsuleOnDisk Flag firstly.
1849 CoDClearCapsuleOnDiskFlag ();
1852 // If Capsule In Ram is supported, delivery capsules through memory
1854 if (PcdGetBool (PcdCapsuleInRamSupport
)) {
1855 DEBUG ((DEBUG_INFO
, "Capsule In Ram is supported, call gRT->UpdateCapsule().\n"));
1856 return RelocateCapsuleToRam (MaxRetry
);
1858 DEBUG ((DEBUG_INFO
, "Reallcoate all Capsule on Disks to %s in RootDir.\n", (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
)));
1859 return RelocateCapsuleToDisk (MaxRetry
);
1864 Remove the temp file from the root of EFI System Partition.
1865 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1866 Function will stall 100ms between each retry.
1868 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1869 devices like USB can get enumerated. Input 0 means no retry.
1871 @retval EFI_SUCCESS Remove the temp file successfully.
1882 UINT16
*LoadOptionNumber
;
1883 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
1884 EFI_HANDLE FsHandle
;
1885 EFI_FILE_HANDLE RootDir
;
1886 EFI_FILE_HANDLE TempCodFile
;
1890 DataSize
= sizeof(UINT16
);
1892 LoadOptionNumber
= AllocatePool (sizeof(UINT16
));
1893 if (LoadOptionNumber
== NULL
) {
1894 return EFI_OUT_OF_RESOURCES
;
1898 // Check if capsule files are relocated
1900 Status
= gRT
->GetVariable (
1901 COD_RELOCATION_LOAD_OPTION_VAR_NAME
,
1902 &gEfiCapsuleVendorGuid
,
1905 (VOID
*)LoadOptionNumber
1907 if (EFI_ERROR(Status
) || DataSize
!= sizeof(UINT16
)) {
1912 // Get the EFI file system from the boot option where the capsules are relocated
1914 Status
= GetEfiSysPartitionFromActiveBootOption(MaxRetry
, &LoadOptionNumber
, &FsHandle
);
1915 if (EFI_ERROR(Status
)) {
1919 Status
= gBS
->HandleProtocol(FsHandle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
1920 if (EFI_ERROR(Status
)) {
1924 Status
= Fs
->OpenVolume(Fs
, &RootDir
);
1925 if (EFI_ERROR(Status
)) {
1930 // Delete the TempCoDFile
1932 Status
= RootDir
->Open(
1935 (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
),
1936 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
,
1939 if (EFI_ERROR(Status
)) {
1943 TempCodFile
->Delete(TempCodFile
);
1946 // Clear "CoDRelocationLoadOption" variable
1948 Status
= gRT
->SetVariable (
1949 COD_RELOCATION_LOAD_OPTION_VAR_NAME
,
1950 &gEfiCapsuleVendorGuid
,
1957 if (LoadOptionNumber
!= NULL
) {
1958 FreePool (LoadOptionNumber
);
1961 if (RootDir
!= NULL
) {
1962 RootDir
->Close(RootDir
);