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 This routine assumes the capsule has been validated by IsValidCapsuleHeader(), so
29 capsule memory overflow is not going to happen in this routine.
31 @param[in] CapsuleHeader Pointer to the capsule header of a capsule name capsule.
32 @param[out] CapsuleNameNum Number of capsule name.
34 @retval NULL Capsule name capsule is not valid.
35 @retval CapsuleNameBuf Array of capsule name physical address.
38 EFI_PHYSICAL_ADDRESS
*
39 ValidateCapsuleNameCapsuleIntegrity (
40 IN EFI_CAPSULE_HEADER
*CapsuleHeader
,
41 OUT UINTN
*CapsuleNameNum
44 UINT8
*CapsuleNamePtr
;
45 UINT8
*CapsuleNameBufStart
;
46 UINT8
*CapsuleNameBufEnd
;
49 EFI_PHYSICAL_ADDRESS
*CapsuleNameBuf
;
51 if (!IsCapsuleNameCapsule (CapsuleHeader
)) {
56 // Total string size must be even.
58 if (((CapsuleHeader
->CapsuleImageSize
- CapsuleHeader
->HeaderSize
) & BIT0
) != 0) {
64 CapsuleNameBufStart
= (UINT8
*) CapsuleHeader
+ CapsuleHeader
->HeaderSize
;
67 // If strings are not aligned on a 16-bit boundary, reallocate memory for it.
69 if (((UINTN
) CapsuleNameBufStart
& BIT0
) != 0) {
70 CapsuleNameBufStart
= AllocateCopyPool (CapsuleHeader
->CapsuleImageSize
- CapsuleHeader
->HeaderSize
, CapsuleNameBufStart
);
71 if (CapsuleNameBufStart
== NULL
) {
76 CapsuleNameBufEnd
= CapsuleNameBufStart
+ CapsuleHeader
->CapsuleImageSize
- CapsuleHeader
->HeaderSize
;
78 CapsuleNamePtr
= CapsuleNameBufStart
;
79 while (CapsuleNamePtr
< CapsuleNameBufEnd
) {
80 StringSize
= StrnSizeS ((CHAR16
*) CapsuleNamePtr
, (CapsuleNameBufEnd
- CapsuleNamePtr
)/sizeof(CHAR16
));
81 CapsuleNamePtr
+= StringSize
;
88 if (CapsuleNamePtr
!= CapsuleNameBufEnd
) {
89 if (CapsuleNameBufStart
!= (UINT8
*)CapsuleHeader
+ CapsuleHeader
->HeaderSize
) {
90 FreePool (CapsuleNameBufStart
);
95 CapsuleNameBuf
= AllocatePool (*CapsuleNameNum
* sizeof (EFI_PHYSICAL_ADDRESS
));
96 if (CapsuleNameBuf
== NULL
) {
97 if (CapsuleNameBufStart
!= (UINT8
*)CapsuleHeader
+ CapsuleHeader
->HeaderSize
) {
98 FreePool (CapsuleNameBufStart
);
103 CapsuleNamePtr
= CapsuleNameBufStart
;
104 while (CapsuleNamePtr
< CapsuleNameBufEnd
) {
105 StringSize
= StrnSizeS ((CHAR16
*) CapsuleNamePtr
, (CapsuleNameBufEnd
- CapsuleNamePtr
)/sizeof(CHAR16
));
106 CapsuleNameBuf
[Index
] = (EFI_PHYSICAL_ADDRESS
)(UINTN
) CapsuleNamePtr
;
107 CapsuleNamePtr
+= StringSize
;
111 return CapsuleNameBuf
;
115 This routine is called to upper case given unicode string.
117 @param[in] Str String to upper case
119 @retval upper cased string after process
130 for (Cptr
= Str
; *Cptr
!= L
'\0'; Cptr
++) {
131 if (L
'a' <= *Cptr
&& *Cptr
<= L
'z') {
132 *Cptr
= *Cptr
- L
'a' + L
'A';
140 This routine is used to return substring before period '.' or '\0'
141 Caller should respsonsible of substr space allocation & free
143 @param[in] Str String to check
144 @param[out] SubStr First part of string before period or '\0'
145 @param[out] SubStrLen Length of first part of string
150 GetSubStringBeforePeriod (
157 for (Index
= 0; Str
[Index
] != L
'.' && Str
[Index
] != L
'\0'; Index
++) {
158 SubStr
[Index
] = Str
[Index
];
161 SubStr
[Index
] = L
'\0';
166 This routine pad the string in tail with input character.
168 @param[in] StrBuf Str buffer to be padded, should be enough room for
169 @param[in] PadLen Expected padding length
170 @param[in] Character Character used to pad
183 for (Index
= 0; StrBuf
[Index
] != L
'\0'; Index
++);
186 StrBuf
[Index
] = Character
;
191 StrBuf
[Index
] = L
'\0';
195 This routine find the offset of the last period '.' of string. If No period exists
196 function FileNameExtension is set to L'\0'
198 @param[in] FileName File name to split between last period
199 @param[out] FileNameFirst First FileName before last period
200 @param[out] FileNameExtension FileName after last period
205 SplitFileNameExtension (
207 OUT CHAR16
*FileNameFirst
,
208 OUT CHAR16
*FileNameExtension
214 StringLen
= StrnLenS(FileName
, MAX_FILE_NAME_SIZE
);
215 for (Index
= StringLen
; Index
> 0 && FileName
[Index
] != L
'.'; Index
--);
218 // No period exists. No FileName Extension
220 if (Index
== 0 && FileName
[Index
] != L
'.') {
221 FileNameExtension
[0] = L
'\0';
224 StrCpyS(FileNameExtension
, MAX_FILE_NAME_SIZE
, &FileName
[Index
+1]);
228 // Copy First file name
230 StrnCpyS(FileNameFirst
, MAX_FILE_NAME_SIZE
, FileName
, Index
);
231 FileNameFirst
[Index
] = L
'\0';
235 This routine is called to get all boot options in the order determnined by:
239 @param[out] OptionBuf BootList buffer to all boot options returned
240 @param[out] OptionCount BootList count of all boot options returned
242 @retval EFI_SUCCESS There is no error when processing capsule
246 GetBootOptionInOrder(
247 OUT EFI_BOOT_MANAGER_LOAD_OPTION
**OptionBuf
,
248 OUT UINTN
*OptionCount
254 CHAR16 BootOptionName
[20];
255 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOrderOptionBuf
;
256 UINTN BootOrderCount
;
257 EFI_BOOT_MANAGER_LOAD_OPTION BootNextOptionEntry
;
259 EFI_BOOT_MANAGER_LOAD_OPTION
*TempBuf
;
261 BootOrderOptionBuf
= NULL
;
269 // First Get BootOption from "BootNext"
271 DataSize
= sizeof(BootNext
);
272 Status
= gRT
->GetVariable (
273 EFI_BOOT_NEXT_VARIABLE_NAME
,
274 &gEfiGlobalVariableGuid
,
280 // BootNext variable is a single UINT16
282 if (!EFI_ERROR(Status
) && DataSize
== sizeof(UINT16
)) {
284 // Add the boot next boot option
286 UnicodeSPrint (BootOptionName
, sizeof (BootOptionName
), L
"Boot%04x", BootNext
);
287 ZeroMem(&BootNextOptionEntry
, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION
));
288 Status
= EfiBootManagerVariableToLoadOption (BootOptionName
, &BootNextOptionEntry
);
290 if (!EFI_ERROR(Status
)) {
296 // Second get BootOption from "BootOrder"
298 BootOrderOptionBuf
= EfiBootManagerGetLoadOptions (&BootOrderCount
, LoadOptionTypeBoot
);
299 if (BootNextCount
== 0 && BootOrderCount
== 0) {
300 return EFI_NOT_FOUND
;
304 // At least one BootOption is found
306 TempBuf
= AllocatePool(sizeof(EFI_BOOT_MANAGER_LOAD_OPTION
) * (BootNextCount
+ BootOrderCount
));
307 if (TempBuf
!= NULL
) {
308 if (BootNextCount
== 1) {
309 CopyMem(TempBuf
, &BootNextOptionEntry
, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION
));
312 if (BootOrderCount
> 0) {
313 CopyMem(TempBuf
+ BootNextCount
, BootOrderOptionBuf
, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION
) * BootOrderCount
);
316 *OptionBuf
= TempBuf
;
317 *OptionCount
= BootNextCount
+ BootOrderCount
;
318 Status
= EFI_SUCCESS
;
320 Status
= EFI_OUT_OF_RESOURCES
;
323 FreePool(BootOrderOptionBuf
);
329 This routine is called to get boot option by OptionNumber.
331 @param[in] Number The OptionNumber of boot option
332 @param[out] OptionBuf BootList buffer to all boot options returned
334 @retval EFI_SUCCESS There is no error when getting boot option
338 GetBootOptionByNumber(
340 OUT EFI_BOOT_MANAGER_LOAD_OPTION
**OptionBuf
344 CHAR16 BootOptionName
[20];
345 EFI_BOOT_MANAGER_LOAD_OPTION BootOption
;
347 UnicodeSPrint (BootOptionName
, sizeof (BootOptionName
), L
"Boot%04x", Number
);
348 ZeroMem (&BootOption
, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION
));
349 Status
= EfiBootManagerVariableToLoadOption (BootOptionName
, &BootOption
);
351 if (!EFI_ERROR (Status
)) {
352 *OptionBuf
= AllocatePool (sizeof (EFI_BOOT_MANAGER_LOAD_OPTION
));
353 if (*OptionBuf
!= NULL
) {
354 CopyMem (*OptionBuf
, &BootOption
, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION
));
355 Status
= EFI_SUCCESS
;
357 Status
= EFI_OUT_OF_RESOURCES
;
365 Get Active EFI System Partition within GPT based on device path.
367 @param[in] DevicePath Device path to find a active EFI System Partition
368 @param[out] FsHandle BootList points to all boot options returned
370 @retval EFI_SUCCESS Active EFI System Partition is succesfully found
371 @retval EFI_NOT_FOUND No Active EFI System Partition is found
375 GetEfiSysPartitionFromDevPath(
376 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
377 OUT EFI_HANDLE
*FsHandle
381 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePath
;
382 HARDDRIVE_DEVICE_PATH
*Hd
;
384 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
387 // Check if the device path contains GPT node
389 TempDevicePath
= DevicePath
;
390 while (!IsDevicePathEnd (TempDevicePath
)) {
391 if ((DevicePathType (TempDevicePath
) == MEDIA_DEVICE_PATH
) &&
392 (DevicePathSubType (TempDevicePath
) == MEDIA_HARDDRIVE_DP
)) {
393 Hd
= (HARDDRIVE_DEVICE_PATH
*)TempDevicePath
;
394 if (Hd
->MBRType
== MBR_TYPE_EFI_PARTITION_TABLE_HEADER
) {
398 TempDevicePath
= NextDevicePathNode (TempDevicePath
);
401 if (!IsDevicePathEnd (TempDevicePath
)) {
403 // Search for EFI system partition protocol on full device path in Boot Option
405 Status
= gBS
->LocateDevicePath (&gEfiPartTypeSystemPartGuid
, &DevicePath
, &Handle
);
408 // Search for simple file system on this handler
410 if (!EFI_ERROR(Status
)) {
411 Status
= gBS
->HandleProtocol(Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
412 if (!EFI_ERROR(Status
)) {
419 return EFI_NOT_FOUND
;
423 This routine is called to get Simple File System protocol on the first EFI system partition found in
424 active boot option. The boot option list is detemined in order by
428 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
429 device like USB can get enumerated.
430 @param[in, out] LoadOptionNumber On input, specify the boot option to get EFI system partition.
431 On output, return the OptionNumber of the boot option where EFI
432 system partition is got from.
433 @param[out] FsFsHandle Simple File System Protocol found on first active EFI system partition
435 @retval EFI_SUCCESS Simple File System protocol found for EFI system partition
436 @retval EFI_NOT_FOUND No Simple File System protocol found for EFI system partition
440 GetEfiSysPartitionFromActiveBootOption(
442 IN OUT UINT16
**LoadOptionNumber
,
443 OUT EFI_HANDLE
*FsHandle
447 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptionBuf
;
450 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
451 EFI_DEVICE_PATH_PROTOCOL
*CurFullPath
;
452 EFI_DEVICE_PATH_PROTOCOL
*PreFullPath
;
457 if (*LoadOptionNumber
!= NULL
) {
459 Status
= GetBootOptionByNumber(**LoadOptionNumber
, &BootOptionBuf
);
460 if (EFI_ERROR(Status
)) {
461 DEBUG ((DEBUG_ERROR
, "GetBootOptionByIndex Failed %x! No BootOption available for connection\n", Status
));
465 Status
= GetBootOptionInOrder(&BootOptionBuf
, &BootOptionNum
);
466 if (EFI_ERROR(Status
)) {
467 DEBUG ((DEBUG_ERROR
, "GetBootOptionInOrder Failed %x! No BootOption available for connection\n", Status
));
473 // Search BootOptionList to check if it is an active boot option with EFI system partition
474 // 1. Connect device path
475 // 2. expend short/plug in devicepath
478 for (Index
= 0; Index
< BootOptionNum
; Index
++) {
480 // Get the boot option from the link list
482 DevicePath
= BootOptionBuf
[Index
].FilePath
;
485 // Skip inactive or legacy boot options
487 if ((BootOptionBuf
[Index
].Attributes
& LOAD_OPTION_ACTIVE
) == 0 ||
488 DevicePathType (DevicePath
) == BBS_DEVICE_PATH
) {
493 CHAR16
*DevicePathStr
;
495 DevicePathStr
= ConvertDevicePathToText(DevicePath
, TRUE
, TRUE
);
496 if (DevicePathStr
!= NULL
){
497 DEBUG((DEBUG_INFO
, "Try BootOption %s\n", DevicePathStr
));
498 FreePool(DevicePathStr
);
500 DEBUG((DEBUG_INFO
, "DevicePathToStr failed\n"));
506 // Try every full device Path generated from bootoption
509 PreFullPath
= CurFullPath
;
510 CurFullPath
= EfiBootManagerGetNextLoadOptionDevicePath(DevicePath
, CurFullPath
);
512 if (PreFullPath
!= NULL
) {
513 FreePool (PreFullPath
);
516 if (CurFullPath
== NULL
) {
518 // No Active EFI system partition is found in BootOption device path
520 Status
= EFI_NOT_FOUND
;
525 CHAR16
*DevicePathStr1
;
527 DevicePathStr1
= ConvertDevicePathToText(CurFullPath
, TRUE
, TRUE
);
528 if (DevicePathStr1
!= NULL
){
529 DEBUG((DEBUG_INFO
, "Full device path %s\n", DevicePathStr1
));
530 FreePool(DevicePathStr1
);
535 // Make sure the boot option device path connected.
536 // Only handle first device in boot option. Other optional device paths are described as OSV specific
537 // FullDevice could contain extra directory & file info. So don't check connection status here.
539 EfiBootManagerConnectDevicePath (CurFullPath
, NULL
);
540 Status
= GetEfiSysPartitionFromDevPath(CurFullPath
, FsHandle
);
543 // Some relocation device like USB need more time to get enumerated
545 while (EFI_ERROR(Status
) && MaxRetry
> 0) {
546 EfiBootManagerConnectDevicePath(CurFullPath
, NULL
);
549 // Search for EFI system partition protocol on full device path in Boot Option
551 Status
= GetEfiSysPartitionFromDevPath(CurFullPath
, FsHandle
);
552 if (!EFI_ERROR(Status
)) {
555 DEBUG((DEBUG_ERROR
, "GetEfiSysPartitionFromDevPath Loop %x\n", Status
));
557 // Stall 100ms if connection failed to ensure USB stack is ready
562 } while(EFI_ERROR(Status
));
565 // Find a qualified Simple File System
567 if (!EFI_ERROR(Status
)) {
574 // Return the OptionNumber of the boot option where EFI system partition is got from
576 if (*LoadOptionNumber
== NULL
) {
577 *LoadOptionNumber
= AllocateCopyPool (sizeof(UINT16
), (UINT16
*) &BootOptionBuf
[Index
].OptionNumber
);
578 if (*LoadOptionNumber
== NULL
) {
579 Status
= EFI_OUT_OF_RESOURCES
;
584 // No qualified EFI system partition found
586 if (*FsHandle
== NULL
) {
587 Status
= EFI_NOT_FOUND
;
591 CHAR16
*DevicePathStr2
;
592 if (*FsHandle
!= NULL
) {
593 DevicePathStr2
= ConvertDevicePathToText(CurFullPath
, TRUE
, TRUE
);
594 if (DevicePathStr2
!= NULL
){
595 DEBUG((DEBUG_INFO
, "Found Active EFI System Partion on %s\n", DevicePathStr2
));
596 FreePool(DevicePathStr2
);
599 DEBUG((DEBUG_INFO
, "Failed to found Active EFI System Partion\n"));
603 if (CurFullPath
!= NULL
) {
604 FreePool(CurFullPath
);
608 // Free BootOption Buffer
610 for (Index
= 0; Index
< BootOptionNum
; Index
++) {
611 if (BootOptionBuf
[Index
].Description
!= NULL
) {
612 FreePool(BootOptionBuf
[Index
].Description
);
615 if (BootOptionBuf
[Index
].FilePath
!= NULL
) {
616 FreePool(BootOptionBuf
[Index
].FilePath
);
619 if (BootOptionBuf
[Index
].OptionalData
!= NULL
) {
620 FreePool(BootOptionBuf
[Index
].OptionalData
);
624 FreePool(BootOptionBuf
);
631 This routine is called to get all file infos with in a given dir & with given file attribute, the file info is listed in
632 alphabetical order described in UEFI spec.
634 @param[in] Dir Directory file handler
635 @param[in] FileAttr Attribute of file to be red from directory
636 @param[out] FileInfoList File images info list red from directory
637 @param[out] FileNum File images number red from directory
639 @retval EFI_SUCCESS File FileInfo list in the given
643 GetFileInfoListInAlphabetFromDir(
644 IN EFI_FILE_HANDLE Dir
,
646 OUT LIST_ENTRY
*FileInfoList
,
651 FILE_INFO_ENTRY
*NewFileInfoEntry
;
652 FILE_INFO_ENTRY
*TempFileInfoEntry
;
653 EFI_FILE_INFO
*FileInfo
;
655 CHAR16
*ListedFileName
;
656 CHAR16
*NewFileNameExtension
;
657 CHAR16
*ListedFileNameExtension
;
658 CHAR16
*TempNewSubStr
;
659 CHAR16
*TempListedSubStr
;
666 UINTN ListedSubStrLen
;
667 INTN SubStrCmpResult
;
669 Status
= EFI_SUCCESS
;
671 ListedFileName
= NULL
;
672 NewFileNameExtension
= NULL
;
673 ListedFileNameExtension
= NULL
;
674 TempNewSubStr
= NULL
;
675 TempListedSubStr
= NULL
;
680 InitializeListHead(FileInfoList
);
682 TempNewSubStr
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
683 TempListedSubStr
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
685 if (TempNewSubStr
== NULL
|| TempListedSubStr
== NULL
) {
686 Status
= EFI_OUT_OF_RESOURCES
;
690 for ( Status
= FileHandleFindFirstFile(Dir
, &FileInfo
)
691 ; !EFI_ERROR(Status
) && !NoFile
692 ; Status
= FileHandleFindNextFile(Dir
, FileInfo
, &NoFile
)
694 if (FileInfo
== NULL
) {
699 // Skip file with mismatching File attribute
701 if ((FileInfo
->Attribute
& (FileAttr
)) == 0) {
705 NewFileInfoEntry
= NULL
;
706 NewFileInfoEntry
= (FILE_INFO_ENTRY
*)AllocateZeroPool(sizeof(FILE_INFO_ENTRY
));
707 if (NewFileInfoEntry
== NULL
) {
708 Status
= EFI_OUT_OF_RESOURCES
;
711 NewFileInfoEntry
->Signature
= FILE_INFO_SIGNATURE
;
712 NewFileInfoEntry
->FileInfo
= AllocateCopyPool((UINTN
) FileInfo
->Size
, FileInfo
);
713 if (NewFileInfoEntry
->FileInfo
== NULL
) {
714 FreePool(NewFileInfoEntry
);
715 Status
= EFI_OUT_OF_RESOURCES
;
719 NewFileInfoEntry
->FileNameFirstPart
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
720 if (NewFileInfoEntry
->FileNameFirstPart
== NULL
) {
721 FreePool(NewFileInfoEntry
->FileInfo
);
722 FreePool(NewFileInfoEntry
);
723 Status
= EFI_OUT_OF_RESOURCES
;
726 NewFileInfoEntry
->FileNameSecondPart
= (CHAR16
*) AllocateZeroPool(MAX_FILE_NAME_SIZE
);
727 if (NewFileInfoEntry
->FileNameSecondPart
== NULL
) {
728 FreePool(NewFileInfoEntry
->FileInfo
);
729 FreePool(NewFileInfoEntry
->FileNameFirstPart
);
730 FreePool(NewFileInfoEntry
);
731 Status
= EFI_OUT_OF_RESOURCES
;
736 // Splitter the whole New file name into 2 parts between the last period L'.' into NewFileName NewFileExtension
737 // If no period in the whole file name. NewFileExtension is set to L'\0'
739 NewFileName
= NewFileInfoEntry
->FileNameFirstPart
;
740 NewFileNameExtension
= NewFileInfoEntry
->FileNameSecondPart
;
741 SplitFileNameExtension(FileInfo
->FileName
, NewFileName
, NewFileNameExtension
);
742 UpperCaseString(NewFileName
);
743 UpperCaseString(NewFileNameExtension
);
746 // Insert capsule file in alphabetical ordered list
748 for (Link
= FileInfoList
->ForwardLink
; Link
!= FileInfoList
; Link
= Link
->ForwardLink
) {
750 // Get the FileInfo from the link list
752 TempFileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
753 ListedFileName
= TempFileInfoEntry
->FileNameFirstPart
;
754 ListedFileNameExtension
= TempFileInfoEntry
->FileNameSecondPart
;
757 // Follow rule in UEFI spec 8.5.5 to compare file name
763 // First compare each substrings in NewFileName & ListedFileName between periods
765 GetSubStringBeforePeriod(&NewFileName
[IndexNew
], TempNewSubStr
, &NewSubStrLen
);
766 GetSubStringBeforePeriod(&ListedFileName
[IndexListed
], TempListedSubStr
, &ListedSubStrLen
);
767 if (NewSubStrLen
> ListedSubStrLen
) {
769 // Substr in NewFileName is longer. Pad tail with SPACE
771 PadStrInTail(TempListedSubStr
, NewSubStrLen
- ListedSubStrLen
, L
' ');
772 } else if (NewSubStrLen
< ListedSubStrLen
){
774 // Substr in ListedFileName is longer. Pad tail with SPACE
776 PadStrInTail(TempNewSubStr
, ListedSubStrLen
- NewSubStrLen
, L
' ');
779 SubStrCmpResult
= StrnCmp(TempNewSubStr
, TempListedSubStr
, MAX_FILE_NAME_LEN
);
780 if (SubStrCmpResult
!= 0) {
785 // Move to skip this substring
787 IndexNew
+= NewSubStrLen
;
788 IndexListed
+= ListedSubStrLen
;
790 // Reach File First Name end
792 if (NewFileName
[IndexNew
] == L
'\0' || ListedFileName
[IndexListed
] == L
'\0') {
797 // Skip the period L'.'
803 if (SubStrCmpResult
< 0) {
805 // NewFileName is smaller. Find the right place to insert New file
808 } else if (SubStrCmpResult
== 0) {
810 // 2 cases whole NewFileName is smaller than ListedFileName
811 // 1. if NewFileName == ListedFileName. Continue to compare FileNameExtension
812 // 2. if NewFileName is shorter than ListedFileName
814 if (NewFileName
[IndexNew
] == L
'\0') {
815 if (ListedFileName
[IndexListed
] != L
'\0' || (StrnCmp(NewFileNameExtension
, ListedFileNameExtension
, MAX_FILE_NAME_LEN
) < 0)) {
822 // Other case, ListedFileName is smaller. Continue to compare the next file in the list
827 // If Find an entry in the list whose name is bigger than new FileInfo in alphabet order
828 // Insert it before this entry
830 // Insert at the tail of this list (Link = FileInfoList)
832 InsertTailList(Link
, &NewFileInfoEntry
->Link
);
837 *FileNum
= FileCount
;
841 if (TempNewSubStr
!= NULL
) {
842 FreePool(TempNewSubStr
);
845 if (TempListedSubStr
!= NULL
) {
846 FreePool(TempListedSubStr
);
849 if (EFI_ERROR(Status
)) {
850 while(!IsListEmpty(FileInfoList
)) {
851 Link
= FileInfoList
->ForwardLink
;
852 RemoveEntryList(Link
);
854 TempFileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
856 FreePool(TempFileInfoEntry
->FileInfo
);
857 FreePool(TempFileInfoEntry
->FileNameFirstPart
);
858 FreePool(TempFileInfoEntry
->FileNameSecondPart
);
859 FreePool(TempFileInfoEntry
);
869 This routine is called to get all qualified image from file from an given directory
870 in alphabetic order. All the file image is copied to allocated boottime memory.
871 Caller should free these memory
873 @param[in] Dir Directory file handler
874 @param[in] FileAttr Attribute of file to be red from directory
875 @param[out] FilePtr File images Info buffer red from directory
876 @param[out] FileNum File images number red from directory
878 @retval EFI_SUCCESS Succeed to get all capsules in alphabetic order.
882 GetFileImageInAlphabetFromDir(
883 IN EFI_FILE_HANDLE Dir
,
885 OUT IMAGE_INFO
**FilePtr
,
891 EFI_FILE_HANDLE FileHandle
;
892 FILE_INFO_ENTRY
*FileInfoEntry
;
893 EFI_FILE_INFO
*FileInfo
;
895 IMAGE_INFO
*TempFilePtrBuf
;
897 LIST_ENTRY FileInfoList
;
901 TempFilePtrBuf
= NULL
;
905 // Get file list in Dir in alphabetical order
907 Status
= GetFileInfoListInAlphabetFromDir(
913 if (EFI_ERROR(Status
)) {
914 DEBUG ((DEBUG_ERROR
, "GetFileInfoListInAlphabetFromDir Failed!\n"));
918 if (FileCount
== 0) {
919 DEBUG ((DEBUG_ERROR
, "No file found in Dir!\n"));
920 Status
= EFI_NOT_FOUND
;
924 TempFilePtrBuf
= (IMAGE_INFO
*)AllocateZeroPool(sizeof(IMAGE_INFO
) * FileCount
);
925 if (TempFilePtrBuf
== NULL
) {
926 Status
= EFI_OUT_OF_RESOURCES
;
931 // Read all files from FileInfoList to BS memory
934 for (Link
= FileInfoList
.ForwardLink
; Link
!= &FileInfoList
; Link
= Link
->ForwardLink
) {
936 // Get FileInfo from the link list
938 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
939 FileInfo
= FileInfoEntry
->FileInfo
;
948 if (EFI_ERROR(Status
)){
952 Size
= (UINTN
)FileInfo
->FileSize
;
953 TempFilePtrBuf
[FileCount
].ImageAddress
= AllocateZeroPool(Size
);
954 if (TempFilePtrBuf
[FileCount
].ImageAddress
== NULL
) {
955 DEBUG((DEBUG_ERROR
, "Fail to allocate memory for capsule. Stop processing the rest.\n"));
959 Status
= FileHandle
->Read(
962 TempFilePtrBuf
[FileCount
].ImageAddress
965 FileHandle
->Close(FileHandle
);
968 // Skip read error file
970 if (EFI_ERROR(Status
) || Size
!= (UINTN
)FileInfo
->FileSize
) {
972 // Remove this error file info accordingly
973 // & move Link to BackLink
975 Link
= RemoveEntryList(Link
);
976 Link
= Link
->BackLink
;
978 FreePool(FileInfoEntry
->FileInfo
);
979 FreePool(FileInfoEntry
->FileNameFirstPart
);
980 FreePool(FileInfoEntry
->FileNameSecondPart
);
981 FreePool(FileInfoEntry
);
983 FreePool(TempFilePtrBuf
[FileCount
].ImageAddress
);
984 TempFilePtrBuf
[FileCount
].ImageAddress
= NULL
;
985 TempFilePtrBuf
[FileCount
].FileInfo
= NULL
;
989 TempFilePtrBuf
[FileCount
].FileInfo
= FileInfo
;
994 for (Link
= FileInfoList
.ForwardLink
; Link
!= &FileInfoList
; Link
= Link
->ForwardLink
) {
995 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
996 FileInfo
= FileInfoEntry
->FileInfo
;
997 DEBUG((DEBUG_INFO
, "Successfully read capsule file %s from disk.\n", FileInfo
->FileName
));
1003 *FilePtr
= TempFilePtrBuf
;
1004 *FileNum
= FileCount
;
1007 // FileInfo will be freed by Calller
1009 while(!IsListEmpty(&FileInfoList
)) {
1010 Link
= FileInfoList
.ForwardLink
;
1011 RemoveEntryList(Link
);
1013 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
1015 FreePool(FileInfoEntry
->FileNameFirstPart
);
1016 FreePool(FileInfoEntry
->FileNameSecondPart
);
1017 FreePool(FileInfoEntry
);
1024 This routine is called to remove all qualified image from file from an given directory.
1026 @param[in] Dir Directory file handler
1027 @param[in] FileAttr Attribute of files to be deleted
1029 @retval EFI_SUCCESS Succeed to remove all files from an given directory.
1034 IN EFI_FILE_HANDLE Dir
,
1040 LIST_ENTRY FileInfoList
;
1041 EFI_FILE_HANDLE FileHandle
;
1042 FILE_INFO_ENTRY
*FileInfoEntry
;
1043 EFI_FILE_INFO
*FileInfo
;
1049 // Get file list in Dir in alphabetical order
1051 Status
= GetFileInfoListInAlphabetFromDir(
1057 if (EFI_ERROR(Status
)) {
1058 DEBUG ((DEBUG_ERROR
, "GetFileInfoListInAlphabetFromDir Failed!\n"));
1062 if (FileCount
== 0) {
1063 DEBUG ((DEBUG_ERROR
, "No file found in Dir!\n"));
1064 Status
= EFI_NOT_FOUND
;
1069 // Delete all files with given attribute in Dir
1071 for (Link
= FileInfoList
.ForwardLink
; Link
!= &(FileInfoList
); Link
= Link
->ForwardLink
) {
1073 // Get FileInfo from the link list
1075 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
1076 FileInfo
= FileInfoEntry
->FileInfo
;
1082 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
,
1085 if (EFI_ERROR(Status
)){
1089 Status
= FileHandle
->Delete(FileHandle
);
1094 while(!IsListEmpty(&FileInfoList
)) {
1095 Link
= FileInfoList
.ForwardLink
;
1096 RemoveEntryList(Link
);
1098 FileInfoEntry
= CR (Link
, FILE_INFO_ENTRY
, Link
, FILE_INFO_SIGNATURE
);
1100 FreePool(FileInfoEntry
->FileInfo
);
1101 FreePool(FileInfoEntry
);
1108 This routine is called to get all caspules from file. The capsule file image is
1109 copied to BS memory. Caller is responsible to free them.
1111 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1112 devices like USB can get enumerated.
1113 @param[out] CapsulePtr Copied Capsule file Image Info buffer
1114 @param[out] CapsuleNum CapsuleNumber
1115 @param[out] FsHandle File system handle
1116 @param[out] LoadOptionNumber OptionNumber of boot option
1118 @retval EFI_SUCCESS Succeed to get all capsules.
1122 GetAllCapsuleOnDisk(
1124 OUT IMAGE_INFO
**CapsulePtr
,
1125 OUT UINTN
*CapsuleNum
,
1126 OUT EFI_HANDLE
*FsHandle
,
1127 OUT UINT16
*LoadOptionNumber
1131 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
1132 EFI_FILE_HANDLE RootDir
;
1133 EFI_FILE_HANDLE FileDir
;
1134 UINT16
*TempOptionNumber
;
1136 TempOptionNumber
= NULL
;
1139 Status
= GetEfiSysPartitionFromActiveBootOption(MaxRetry
, &TempOptionNumber
, FsHandle
);
1140 if (EFI_ERROR(Status
)) {
1144 Status
= gBS
->HandleProtocol(*FsHandle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
1145 if (EFI_ERROR(Status
)) {
1149 Status
= Fs
->OpenVolume(Fs
, &RootDir
);
1150 if (EFI_ERROR(Status
)) {
1154 Status
= RootDir
->Open(
1157 EFI_CAPSULE_FILE_DIRECTORY
,
1161 if (EFI_ERROR(Status
)) {
1162 DEBUG((DEBUG_ERROR
, "CodLibGetAllCapsuleOnDisk fail to open RootDir!\n"));
1163 RootDir
->Close (RootDir
);
1166 RootDir
->Close (RootDir
);
1169 // Only Load files with EFI_FILE_SYSTEM or EFI_FILE_ARCHIVE attribute
1170 // ignore EFI_FILE_READ_ONLY, EFI_FILE_HIDDEN, EFI_FILE_RESERVED, EFI_FILE_DIRECTORY
1172 Status
= GetFileImageInAlphabetFromDir(
1174 EFI_FILE_SYSTEM
| EFI_FILE_ARCHIVE
,
1178 DEBUG((DEBUG_INFO
, "GetFileImageInAlphabetFromDir status %x\n", Status
));
1181 // Always remove file to avoid deadloop in capsule process
1183 Status
= RemoveFileFromDir(FileDir
, EFI_FILE_SYSTEM
| EFI_FILE_ARCHIVE
);
1184 DEBUG((DEBUG_INFO
, "RemoveFileFromDir status %x\n", Status
));
1186 FileDir
->Close (FileDir
);
1188 if (LoadOptionNumber
!= NULL
) {
1189 *LoadOptionNumber
= *TempOptionNumber
;
1196 Build Gather list for a list of capsule images.
1198 @param[in] CapsuleBuffer An array of pointer to capsule images
1199 @param[in] CapsuleSize An array of UINTN to capsule images size
1200 @param[in] CapsuleNum The count of capsule images
1201 @param[out] BlockDescriptors The block descriptors for the capsule images
1203 @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.
1208 IN VOID
**CapsuleBuffer
,
1209 IN UINTN
*CapsuleSize
,
1210 IN UINTN CapsuleNum
,
1211 OUT EFI_CAPSULE_BLOCK_DESCRIPTOR
**BlockDescriptors
1215 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptors1
;
1216 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptorPre
;
1217 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptorsHeader
;
1220 BlockDescriptors1
= NULL
;
1221 BlockDescriptorPre
= NULL
;
1222 BlockDescriptorsHeader
= NULL
;
1224 for (Index
= 0; Index
< CapsuleNum
; Index
++) {
1226 // Allocate memory for the descriptors.
1228 BlockDescriptors1
= AllocateZeroPool (2 * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR
));
1229 if (BlockDescriptors1
== NULL
) {
1230 DEBUG ((DEBUG_ERROR
, "BuildGatherList: failed to allocate memory for descriptors\n"));
1231 Status
= EFI_OUT_OF_RESOURCES
;
1234 DEBUG ((DEBUG_INFO
, "BuildGatherList: creating capsule descriptors at 0x%X\n", (UINTN
) BlockDescriptors1
));
1238 // Record descirptor header
1241 BlockDescriptorsHeader
= BlockDescriptors1
;
1244 if (BlockDescriptorPre
!= NULL
) {
1245 BlockDescriptorPre
->Union
.ContinuationPointer
= (UINTN
) BlockDescriptors1
;
1246 BlockDescriptorPre
->Length
= 0;
1249 BlockDescriptors1
->Union
.DataBlock
= (UINTN
) CapsuleBuffer
[Index
];
1250 BlockDescriptors1
->Length
= CapsuleSize
[Index
];
1252 BlockDescriptorPre
= BlockDescriptors1
+ 1;
1253 BlockDescriptors1
= NULL
;
1259 if (BlockDescriptorPre
!= NULL
) {
1260 BlockDescriptorPre
->Union
.ContinuationPointer
= (UINTN
)NULL
;
1261 BlockDescriptorPre
->Length
= 0;
1262 *BlockDescriptors
= BlockDescriptorsHeader
;
1268 if (BlockDescriptors1
!= NULL
) {
1269 FreePool (BlockDescriptors1
);
1276 This routine is called to check if CapsuleOnDisk flag in OsIndications Variable
1279 @retval TRUE Flag is enabled
1280 @retval FALSE Flag is not enabled
1285 CoDCheckCapsuleOnDiskFlag(
1290 UINT64 OsIndication
;
1294 // Check File Capsule Delivery Supported Flag in OsIndication variable
1297 DataSize
= sizeof(UINT64
);
1298 Status
= gRT
->GetVariable (
1299 EFI_OS_INDICATIONS_VARIABLE_NAME
,
1300 &gEfiGlobalVariableGuid
,
1305 if (!EFI_ERROR(Status
) &&
1306 (OsIndication
& EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
) != 0) {
1315 This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable.
1317 @retval EFI_SUCCESS All Capsule On Disk flags are cleared
1322 CoDClearCapsuleOnDiskFlag(
1327 UINT64 OsIndication
;
1331 // Reset File Capsule Delivery Supported Flag in OsIndication variable
1334 DataSize
= sizeof(UINT64
);
1335 Status
= gRT
->GetVariable (
1336 EFI_OS_INDICATIONS_VARIABLE_NAME
,
1337 &gEfiGlobalVariableGuid
,
1342 if (EFI_ERROR(Status
) ||
1343 (OsIndication
& EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
) == 0) {
1347 OsIndication
&= ~((UINT64
)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
);
1348 Status
= gRT
->SetVariable (
1349 EFI_OS_INDICATIONS_VARIABLE_NAME
,
1350 &gEfiGlobalVariableGuid
,
1351 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
1355 ASSERT(!EFI_ERROR(Status
));
1358 // Delete BootNext variable. Capsule Process may reset system, so can't rely on Bds to clear this variable
1360 Status
= gRT
->SetVariable (
1361 EFI_BOOT_NEXT_VARIABLE_NAME
,
1362 &gEfiGlobalVariableGuid
,
1367 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_NOT_FOUND
);
1373 This routine is called to clear CapsuleOnDisk Relocation Info variable.
1374 Total Capsule On Disk length is recorded in this variable
1376 @retval EFI_SUCCESS Capsule On Disk flags are cleared
1380 CoDClearCapsuleRelocationInfo(
1384 return gRT
->SetVariable (
1385 COD_RELOCATION_INFO_VAR_NAME
,
1386 &gEfiCapsuleVendorGuid
,
1394 Relocate Capsule on Disk from EFI system partition to a platform-specific NV storage device
1395 with BlockIo protocol. Relocation device path, identified by PcdCodRelocationDevPath, must
1396 be a full device path.
1397 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1398 Function will stall 100ms between each retry.
1401 Content corruption. Block IO write directly touches low level write. Orignal partitions, file systems
1402 of the relocation device will be corrupted.
1404 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1405 devices like USB can get enumerated.
1407 @retval EFI_SUCCESS Capsule on Disk images are sucessfully relocated to the platform-specific device.
1411 RelocateCapsuleToDisk(
1416 UINTN CapsuleOnDiskNum
;
1419 UINT64 TotalImageSize
;
1420 UINT64 TotalImageNameSize
;
1421 IMAGE_INFO
*CapsuleOnDiskBuf
;
1423 EFI_HANDLE TempHandle
;
1424 EFI_HANDLE
*HandleBuffer
;
1425 UINTN NumberOfHandles
;
1426 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
1427 UINT8
*CapsuleDataBuf
;
1429 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
1430 EFI_FILE_HANDLE RootDir
;
1431 EFI_FILE_HANDLE TempCodFile
;
1432 UINT64 TempCodFileSize
;
1433 EFI_DEVICE_PATH
*TempDevicePath
;
1434 BOOLEAN RelocationInfo
;
1435 UINT16 LoadOptionNumber
;
1436 EFI_CAPSULE_HEADER FileNameCapsuleHeader
;
1440 HandleBuffer
= NULL
;
1441 CapsuleDataBuf
= NULL
;
1442 CapsuleOnDiskBuf
= NULL
;
1443 NumberOfHandles
= 0;
1445 DEBUG ((DEBUG_INFO
, "CapsuleOnDisk RelocateCapsule Enter\n"));
1448 // 1. Load all Capsule On Disks in to memory
1450 Status
= GetAllCapsuleOnDisk(MaxRetry
, &CapsuleOnDiskBuf
, &CapsuleOnDiskNum
, &Handle
, &LoadOptionNumber
);
1451 if (EFI_ERROR(Status
) || CapsuleOnDiskNum
== 0 || CapsuleOnDiskBuf
== NULL
) {
1452 DEBUG ((DEBUG_INFO
, "RelocateCapsule: GetAllCapsuleOnDisk Status - 0x%x\n", Status
));
1453 return EFI_NOT_FOUND
;
1457 // 2. Connect platform special device path as relocation device.
1458 // If no platform special device path specified or the device path is invalid, use the EFI system partition where
1459 // stores the capsules as relocation device.
1461 if (IsDevicePathValid ((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
), PcdGetSize(PcdCodRelocationDevPath
))) {
1462 Status
= EfiBootManagerConnectDevicePath ((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
), &TempHandle
);
1463 if (EFI_ERROR(Status
)) {
1464 DEBUG ((DEBUG_ERROR
, "RelocateCapsule: EfiBootManagerConnectDevicePath Status - 0x%x\n", Status
));
1469 // Connect all the child handle. Partition & FAT drivers are allowed in this case
1471 gBS
->ConnectController (TempHandle
, NULL
, NULL
, TRUE
);
1472 Status
= gBS
->LocateHandleBuffer(
1474 &gEfiSimpleFileSystemProtocolGuid
,
1479 if (EFI_ERROR(Status
)) {
1480 DEBUG ((DEBUG_ERROR
, "RelocateCapsule: LocateHandleBuffer Status - 0x%x\n", Status
));
1485 // Find first Simple File System Handle which can match PcdCodRelocationDevPath
1487 for (Index
= 0; Index
< NumberOfHandles
; Index
++) {
1488 Status
= gBS
->HandleProtocol(HandleBuffer
[Index
], &gEfiDevicePathProtocolGuid
, (VOID
**)&TempDevicePath
);
1489 if (EFI_ERROR(Status
)) {
1493 DataSize
= GetDevicePathSize((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
)) - sizeof(EFI_DEVICE_PATH
);
1494 if (0 == CompareMem((EFI_DEVICE_PATH
*)PcdGetPtr(PcdCodRelocationDevPath
), TempDevicePath
, DataSize
)) {
1495 Handle
= HandleBuffer
[Index
];
1500 FreePool(HandleBuffer
);
1502 if (Index
== NumberOfHandles
) {
1503 DEBUG ((DEBUG_ERROR
, "RelocateCapsule: No simple file system protocol found.\n"));
1504 Status
= EFI_NOT_FOUND
;
1508 Status
= gBS
->HandleProtocol(Handle
, &gEfiBlockIoProtocolGuid
, (VOID
**)&BlockIo
);
1509 if (EFI_ERROR(Status
) || BlockIo
->Media
->ReadOnly
) {
1510 DEBUG((DEBUG_ERROR
, "Fail to find Capsule on Disk relocation BlockIo device or device is ReadOnly!\n"));
1514 Status
= gBS
->HandleProtocol(Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
1515 if (EFI_ERROR(Status
)) {
1520 // Check if device used to relocate Capsule On Disk is big enough
1523 TotalImageNameSize
= 0;
1524 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1528 if (MAX_ADDRESS
- (UINTN
)TotalImageSize
<= CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
) {
1529 Status
= EFI_INVALID_PARAMETER
;
1533 if (MAX_ADDRESS
- (UINTN
)TotalImageNameSize
<= StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
)) {
1534 Status
= EFI_INVALID_PARAMETER
;
1538 TotalImageSize
+= CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
;
1539 TotalImageNameSize
+= StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1540 DEBUG((DEBUG_INFO
, "RelocateCapsule: %x Size %x\n",CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
, CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
));
1543 DEBUG((DEBUG_INFO
, "RelocateCapsule: TotalImageSize %x\n", TotalImageSize
));
1544 DEBUG((DEBUG_INFO
, "RelocateCapsule: TotalImageNameSize %x\n", TotalImageNameSize
));
1546 if (MAX_ADDRESS
- (UINTN
)TotalImageNameSize
<= sizeof(UINT64
) * 2 ||
1547 MAX_ADDRESS
- (UINTN
)TotalImageSize
<= (UINTN
)TotalImageNameSize
+ sizeof(UINT64
) * 2) {
1548 Status
= EFI_INVALID_PARAMETER
;
1552 TempCodFileSize
= sizeof(UINT64
) + TotalImageSize
+ sizeof(EFI_CAPSULE_HEADER
) + TotalImageNameSize
;
1555 // Check if CapsuleTotalSize. There could be reminder, so use LastBlock number directly
1557 if (DivU64x32(TempCodFileSize
, BlockIo
->Media
->BlockSize
) > BlockIo
->Media
->LastBlock
) {
1558 DEBUG((DEBUG_ERROR
, "RelocateCapsule: Relocation device isn't big enough to hold all Capsule on Disk!\n"));
1559 DEBUG((DEBUG_ERROR
, "TotalImageSize = %x\n", TotalImageSize
));
1560 DEBUG((DEBUG_ERROR
, "TotalImageNameSize = %x\n", TotalImageNameSize
));
1561 DEBUG((DEBUG_ERROR
, "RelocationDev BlockSize = %x LastBlock = %x\n", BlockIo
->Media
->BlockSize
, BlockIo
->Media
->LastBlock
));
1562 Status
= EFI_OUT_OF_RESOURCES
;
1566 CapsuleDataBuf
= AllocatePool((UINTN
) TempCodFileSize
);
1567 if (CapsuleDataBuf
== NULL
) {
1568 Status
= EFI_OUT_OF_RESOURCES
;
1573 // First UINT64 reserved for total image size, including capsule name capsule.
1575 *(UINT64
*) CapsuleDataBuf
= TotalImageSize
+ sizeof(EFI_CAPSULE_HEADER
) + TotalImageNameSize
;
1578 // Line up all the Capsule on Disk and write to relocation disk at one time. It could save some time in disk write
1580 for (Index
= 0, CapsulePtr
= CapsuleDataBuf
+ sizeof(UINT64
); Index
< CapsuleOnDiskNum
; Index
++) {
1581 CopyMem(CapsulePtr
, CapsuleOnDiskBuf
[Index
].ImageAddress
, (UINTN
) CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
);
1582 CapsulePtr
+= CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
;
1586 // Line the capsule header for capsule name capsule.
1588 CopyGuid(&FileNameCapsuleHeader
.CapsuleGuid
, &gEdkiiCapsuleOnDiskNameGuid
);
1589 FileNameCapsuleHeader
.CapsuleImageSize
= (UINT32
) TotalImageNameSize
+ sizeof(EFI_CAPSULE_HEADER
);
1590 FileNameCapsuleHeader
.Flags
= CAPSULE_FLAGS_PERSIST_ACROSS_RESET
;
1591 FileNameCapsuleHeader
.HeaderSize
= sizeof(EFI_CAPSULE_HEADER
);
1592 CopyMem(CapsulePtr
, &FileNameCapsuleHeader
, FileNameCapsuleHeader
.HeaderSize
);
1593 CapsulePtr
+= FileNameCapsuleHeader
.HeaderSize
;
1596 // Line up all the Capsule file names.
1598 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1599 CopyMem(CapsulePtr
, CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
, StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
));
1600 CapsulePtr
+= StrSize(CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1604 // 5. Flash all Capsules on Disk to TempCoD.tmp under RootDir
1606 Status
= Fs
->OpenVolume(Fs
, &RootDir
);
1607 if (EFI_ERROR(Status
)) {
1608 DEBUG((DEBUG_ERROR
, "RelocateCapsule: OpenVolume error. %x\n", Status
));
1612 Status
= RootDir
->Open(
1615 (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
),
1616 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
,
1619 if (!EFI_ERROR(Status
)) {
1621 // Error handling code to prevent malicious code to hold this file to block capsule on disk
1623 TempCodFile
->Delete(TempCodFile
);
1625 Status
= RootDir
->Open(
1628 (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
),
1629 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
,
1632 if (EFI_ERROR(Status
)) {
1633 DEBUG((DEBUG_ERROR
, "RelocateCapsule: Open TemCoD.tmp error. %x\n", Status
));
1638 // Always write at the begining of TempCap file
1640 DataSize
= (UINTN
) TempCodFileSize
;
1641 Status
= TempCodFile
->Write(
1646 if (EFI_ERROR(Status
)) {
1647 DEBUG((DEBUG_ERROR
, "RelocateCapsule: Write TemCoD.tmp error. %x\n", Status
));
1651 if (DataSize
!= TempCodFileSize
) {
1652 Status
= EFI_DEVICE_ERROR
;
1657 // Save Capsule On Disk relocation info to "CodRelocationInfo" Var
1658 // It is used in next reboot by TCB
1660 RelocationInfo
= TRUE
;
1661 Status
= gRT
->SetVariable(
1662 COD_RELOCATION_INFO_VAR_NAME
,
1663 &gEfiCapsuleVendorGuid
,
1664 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
1669 // Save the LoadOptionNumber of the boot option, where the capsule is relocated,
1670 // into "CodRelocationLoadOption" var. It is used in next reboot after capsule is
1671 // updated out of TCB to remove the TempCoDFile.
1673 Status
= gRT
->SetVariable(
1674 COD_RELOCATION_LOAD_OPTION_VAR_NAME
,
1675 &gEfiCapsuleVendorGuid
,
1676 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
1683 if (CapsuleDataBuf
!= NULL
) {
1684 FreePool(CapsuleDataBuf
);
1687 if (CapsuleOnDiskBuf
!= NULL
) {
1689 // Free resources allocated by CodLibGetAllCapsuleOnDisk
1691 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++ ) {
1692 FreePool(CapsuleOnDiskBuf
[Index
].ImageAddress
);
1693 FreePool(CapsuleOnDiskBuf
[Index
].FileInfo
);
1695 FreePool(CapsuleOnDiskBuf
);
1698 if (TempCodFile
!= NULL
) {
1699 if (EFI_ERROR(Status
)) {
1700 TempCodFile
->Delete (TempCodFile
);
1702 TempCodFile
->Close (TempCodFile
);
1706 if (RootDir
!= NULL
) {
1707 RootDir
->Close (RootDir
);
1714 For the platforms that support Capsule In Ram, reuse the Capsule In Ram to deliver capsule.
1715 Relocate Capsule On Disk to memory and call UpdateCapsule().
1716 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1717 Function will stall 100ms between each retry.
1719 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1720 devices like USB can get enumerated.
1722 @retval EFI_SUCCESS Deliver capsule through Capsule In Ram successfully.
1726 RelocateCapsuleToRam (
1731 UINTN CapsuleOnDiskNum
;
1732 IMAGE_INFO
*CapsuleOnDiskBuf
;
1734 EFI_CAPSULE_BLOCK_DESCRIPTOR
*BlockDescriptors
;
1735 VOID
**CapsuleBuffer
;
1737 EFI_CAPSULE_HEADER
*FileNameCapsule
;
1741 UINTN TotalStringSize
;
1742 UINTN CapsulesToProcess
;
1744 CapsuleOnDiskBuf
= NULL
;
1745 BlockDescriptors
= NULL
;
1746 CapsuleBuffer
= NULL
;
1748 FileNameCapsule
= NULL
;
1749 TotalStringSize
= 0;
1752 // 1. Load all Capsule On Disks into memory
1754 Status
= GetAllCapsuleOnDisk (MaxRetry
, &CapsuleOnDiskBuf
, &CapsuleOnDiskNum
, &Handle
, NULL
);
1755 if (EFI_ERROR (Status
) || CapsuleOnDiskNum
== 0 || CapsuleOnDiskBuf
== NULL
) {
1756 DEBUG ((DEBUG_ERROR
, "GetAllCapsuleOnDisk Status - 0x%x\n", Status
));
1757 return EFI_NOT_FOUND
;
1761 // 2. Add a capsule for Capsule file name strings
1763 CapsuleBuffer
= AllocateZeroPool ((CapsuleOnDiskNum
+ 1) * sizeof (VOID
*));
1764 if (CapsuleBuffer
== NULL
) {
1765 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory for capsules.\n"));
1766 return EFI_OUT_OF_RESOURCES
;
1769 CapsuleSize
= AllocateZeroPool ((CapsuleOnDiskNum
+ 1) * sizeof (UINTN
));
1770 if (CapsuleSize
== NULL
) {
1771 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory for capsules.\n"));
1772 FreePool (CapsuleBuffer
);
1773 return EFI_OUT_OF_RESOURCES
;
1776 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1777 CapsuleBuffer
[Index
] = (VOID
*)(UINTN
) CapsuleOnDiskBuf
[Index
].ImageAddress
;
1778 CapsuleSize
[Index
] = (UINTN
) CapsuleOnDiskBuf
[Index
].FileInfo
->FileSize
;
1779 TotalStringSize
+= StrSize (CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1782 // If Persist Across Reset isn't supported, skip the file name strings capsule
1783 if (!FeaturePcdGet (PcdSupportUpdateCapsuleReset
)) {
1784 CapsulesToProcess
= CapsuleOnDiskNum
;
1787 CapsulesToProcess
= CapsuleOnDiskNum
+ 1;
1789 FileNameCapsule
= AllocateZeroPool (sizeof (EFI_CAPSULE_HEADER
) + TotalStringSize
);
1790 if (FileNameCapsule
== NULL
) {
1791 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory for name capsule.\n"));
1792 FreePool (CapsuleBuffer
);
1793 FreePool (CapsuleSize
);
1794 return EFI_OUT_OF_RESOURCES
;
1797 FileNameCapsule
->CapsuleImageSize
= (UINT32
) (sizeof (EFI_CAPSULE_HEADER
) + TotalStringSize
);
1798 FileNameCapsule
->Flags
= CAPSULE_FLAGS_PERSIST_ACROSS_RESET
;
1799 FileNameCapsule
->HeaderSize
= sizeof (EFI_CAPSULE_HEADER
);
1800 CopyGuid (&(FileNameCapsule
->CapsuleGuid
), &gEdkiiCapsuleOnDiskNameGuid
);
1802 StringBuf
= (UINT8
*)FileNameCapsule
+ FileNameCapsule
->HeaderSize
;
1803 for (Index
= 0; Index
< CapsuleOnDiskNum
; Index
++) {
1804 StringSize
= StrSize (CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
);
1805 CopyMem (StringBuf
, CapsuleOnDiskBuf
[Index
].FileInfo
->FileName
, StringSize
);
1806 StringBuf
+= StringSize
;
1809 CapsuleBuffer
[CapsuleOnDiskNum
] = FileNameCapsule
;
1810 CapsuleSize
[CapsuleOnDiskNum
] = TotalStringSize
+ sizeof (EFI_CAPSULE_HEADER
);
1813 // 3. Build Gather list for the capsules
1816 Status
= BuildGatherList (CapsuleBuffer
, CapsuleSize
, CapsulesToProcess
, &BlockDescriptors
);
1817 if (EFI_ERROR (Status
) || BlockDescriptors
== NULL
) {
1818 FreePool (CapsuleBuffer
);
1819 FreePool (CapsuleSize
);
1820 if (FileNameCapsule
!= NULL
) {
1821 FreePool (FileNameCapsule
);
1823 return EFI_OUT_OF_RESOURCES
;
1827 // 4. Call UpdateCapsule() service
1829 Status
= gRT
->UpdateCapsule ((EFI_CAPSULE_HEADER
**) CapsuleBuffer
,
1831 (UINTN
) BlockDescriptors
);
1837 Relocate Capsule on Disk from EFI system partition.
1839 Two solution to deliver Capsule On Disk:
1840 Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().
1841 Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage
1842 device with BlockIo protocol.
1844 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1845 Function will stall 100ms between each retry.
1848 Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.
1849 Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file
1850 systems of the relocation device will be corrupted.
1852 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1853 devices like USB can get enumerated. Input 0 means no retry.
1855 @retval EFI_SUCCESS Capsule on Disk images are successfully relocated.
1864 if (!PcdGetBool (PcdCapsuleOnDiskSupport
)) {
1865 return EFI_UNSUPPORTED
;
1869 // Clear CapsuleOnDisk Flag firstly.
1871 CoDClearCapsuleOnDiskFlag ();
1874 // If Capsule In Ram is supported, delivery capsules through memory
1876 if (PcdGetBool (PcdCapsuleInRamSupport
)) {
1877 DEBUG ((DEBUG_INFO
, "Capsule In Ram is supported, call gRT->UpdateCapsule().\n"));
1878 return RelocateCapsuleToRam (MaxRetry
);
1880 DEBUG ((DEBUG_INFO
, "Reallcoate all Capsule on Disks to %s in RootDir.\n", (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
)));
1881 return RelocateCapsuleToDisk (MaxRetry
);
1886 Remove the temp file from the root of EFI System Partition.
1887 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
1888 Function will stall 100ms between each retry.
1890 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
1891 devices like USB can get enumerated. Input 0 means no retry.
1893 @retval EFI_SUCCESS Remove the temp file successfully.
1904 UINT16
*LoadOptionNumber
;
1905 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
1906 EFI_HANDLE FsHandle
;
1907 EFI_FILE_HANDLE RootDir
;
1908 EFI_FILE_HANDLE TempCodFile
;
1912 DataSize
= sizeof(UINT16
);
1914 LoadOptionNumber
= AllocatePool (sizeof(UINT16
));
1915 if (LoadOptionNumber
== NULL
) {
1916 return EFI_OUT_OF_RESOURCES
;
1920 // Check if capsule files are relocated
1922 Status
= gRT
->GetVariable (
1923 COD_RELOCATION_LOAD_OPTION_VAR_NAME
,
1924 &gEfiCapsuleVendorGuid
,
1927 (VOID
*)LoadOptionNumber
1929 if (EFI_ERROR(Status
) || DataSize
!= sizeof(UINT16
)) {
1934 // Get the EFI file system from the boot option where the capsules are relocated
1936 Status
= GetEfiSysPartitionFromActiveBootOption(MaxRetry
, &LoadOptionNumber
, &FsHandle
);
1937 if (EFI_ERROR(Status
)) {
1941 Status
= gBS
->HandleProtocol(FsHandle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
1942 if (EFI_ERROR(Status
)) {
1946 Status
= Fs
->OpenVolume(Fs
, &RootDir
);
1947 if (EFI_ERROR(Status
)) {
1952 // Delete the TempCoDFile
1954 Status
= RootDir
->Open(
1957 (CHAR16
*)PcdGetPtr(PcdCoDRelocationFileName
),
1958 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
,
1961 if (EFI_ERROR(Status
)) {
1965 TempCodFile
->Delete(TempCodFile
);
1968 // Clear "CoDRelocationLoadOption" variable
1970 Status
= gRT
->SetVariable (
1971 COD_RELOCATION_LOAD_OPTION_VAR_NAME
,
1972 &gEfiCapsuleVendorGuid
,
1979 if (LoadOptionNumber
!= NULL
) {
1980 FreePool (LoadOptionNumber
);
1983 if (RootDir
!= NULL
) {
1984 RootDir
->Close(RootDir
);