2 Source file for CD recovery PEIM
4 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include "PeiCdExpress.h"
12 PEI_CD_EXPRESS_PRIVATE_DATA
*mPrivateData
= NULL
;
13 CHAR8
*mRecoveryFileName
;
14 UINTN mRecoveryFileNameSize
;
17 Installs the Device Recovery Module PPI, Initialize BlockIo Ppi
18 installation notification
20 @param FileHandle The file handle of the image.
21 @param PeiServices General purpose services available to every PEIM.
23 @retval EFI_SUCCESS The function completed successfully.
24 @retval EFI_OUT_OF_RESOURCES There is not enough system memory.
30 IN EFI_PEI_FILE_HANDLE FileHandle
,
31 IN CONST EFI_PEI_SERVICES
**PeiServices
35 PEI_CD_EXPRESS_PRIVATE_DATA
*PrivateData
;
37 if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle
))) {
41 PrivateData
= AllocatePages (EFI_SIZE_TO_PAGES (sizeof (*PrivateData
)));
42 if (PrivateData
== NULL
) {
43 return EFI_OUT_OF_RESOURCES
;
46 mRecoveryFileNameSize
= PcdGetSize (PcdRecoveryFileName
) / sizeof (CHAR16
);
47 mRecoveryFileName
= AllocatePool (mRecoveryFileNameSize
);
48 if (mRecoveryFileName
== NULL
) {
49 return EFI_OUT_OF_RESOURCES
;
52 Status
= UnicodeStrToAsciiStrS (PcdGetPtr (PcdRecoveryFileName
), mRecoveryFileName
, mRecoveryFileNameSize
);
53 if (EFI_ERROR (Status
)) {
58 // Initialize Private Data (to zero, as is required by subsequent operations)
60 ZeroMem (PrivateData
, sizeof (*PrivateData
));
61 PrivateData
->Signature
= PEI_CD_EXPRESS_PRIVATE_DATA_SIGNATURE
;
63 PrivateData
->BlockBuffer
= AllocatePages (EFI_SIZE_TO_PAGES (PEI_CD_BLOCK_SIZE
));
64 if (PrivateData
->BlockBuffer
== NULL
) {
65 return EFI_OUT_OF_RESOURCES
;
68 PrivateData
->CapsuleCount
= 0;
69 Status
= UpdateBlocksAndVolumes (PrivateData
, TRUE
);
70 Status
= UpdateBlocksAndVolumes (PrivateData
, FALSE
);
75 PrivateData
->DeviceRecoveryPpi
.GetNumberRecoveryCapsules
= GetNumberRecoveryCapsules
;
76 PrivateData
->DeviceRecoveryPpi
.GetRecoveryCapsuleInfo
= GetRecoveryCapsuleInfo
;
77 PrivateData
->DeviceRecoveryPpi
.LoadRecoveryCapsule
= LoadRecoveryCapsule
;
79 PrivateData
->PpiDescriptor
.Flags
= (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
);
80 PrivateData
->PpiDescriptor
.Guid
= &gEfiPeiDeviceRecoveryModulePpiGuid
;
81 PrivateData
->PpiDescriptor
.Ppi
= &PrivateData
->DeviceRecoveryPpi
;
83 Status
= PeiServicesInstallPpi (&PrivateData
->PpiDescriptor
);
84 if (EFI_ERROR (Status
)) {
85 return EFI_OUT_OF_RESOURCES
;
89 // PrivateData is allocated now, set it to the module variable
91 mPrivateData
= PrivateData
;
94 // Installs Block Io Ppi notification function
96 PrivateData
->NotifyDescriptor
.Flags
=
98 EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK
100 PrivateData
->NotifyDescriptor
.Guid
= &gEfiPeiVirtualBlockIoPpiGuid
;
101 PrivateData
->NotifyDescriptor
.Notify
= BlockIoNotifyEntry
;
103 PrivateData
->NotifyDescriptor2
.Flags
=
105 EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK
|
106 EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
108 PrivateData
->NotifyDescriptor2
.Guid
= &gEfiPeiVirtualBlockIo2PpiGuid
;
109 PrivateData
->NotifyDescriptor2
.Notify
= BlockIoNotifyEntry
;
111 return PeiServicesNotifyPpi (&PrivateData
->NotifyDescriptor
);
115 BlockIo installation notification function.
117 This function finds out all the current Block IO PPIs in the system and add them
120 @param PeiServices Indirect reference to the PEI Services Table.
121 @param NotifyDescriptor Address of the notification descriptor data structure.
122 @param Ppi Address of the PPI that was installed.
124 @retval EFI_SUCCESS The function completes successfully.
130 IN EFI_PEI_SERVICES
**PeiServices
,
131 IN EFI_PEI_NOTIFY_DESCRIPTOR
*NotifyDescriptor
,
135 if (CompareGuid (NotifyDescriptor
->Guid
, &gEfiPeiVirtualBlockIo2PpiGuid
)) {
136 UpdateBlocksAndVolumes (mPrivateData
, TRUE
);
138 UpdateBlocksAndVolumes (mPrivateData
, FALSE
);
145 Finds out all the current Block IO PPIs in the system and add them into private data.
147 @param PrivateData The private data structure that contains recovery module information.
148 @param BlockIo2 Boolean to show whether using BlockIo2 or BlockIo.
150 @retval EFI_SUCCESS The blocks and volumes are updated successfully.
154 UpdateBlocksAndVolumes (
155 IN OUT PEI_CD_EXPRESS_PRIVATE_DATA
*PrivateData
,
160 EFI_PEI_PPI_DESCRIPTOR
*TempPpiDescriptor
;
161 UINTN BlockIoPpiInstance
;
162 EFI_PEI_RECOVERY_BLOCK_IO_PPI
*BlockIoPpi
;
163 EFI_PEI_RECOVERY_BLOCK_IO2_PPI
*BlockIo2Ppi
;
164 UINTN NumberBlockDevices
;
165 UINTN IndexBlockDevice
;
166 EFI_PEI_BLOCK_IO_MEDIA Media
;
167 EFI_PEI_BLOCK_IO2_MEDIA Media2
;
168 EFI_PEI_SERVICES
**PeiServices
;
170 IndexBlockDevice
= 0;
174 // Find out all Block Io Ppi instances within the system
175 // Assuming all device Block Io Peims are dispatched already
177 for (BlockIoPpiInstance
= 0; BlockIoPpiInstance
< PEI_CD_EXPRESS_MAX_BLOCK_IO_PPI
; BlockIoPpiInstance
++) {
179 Status
= PeiServicesLocatePpi (
180 &gEfiPeiVirtualBlockIo2PpiGuid
,
183 (VOID
**)&BlockIo2Ppi
186 Status
= PeiServicesLocatePpi (
187 &gEfiPeiVirtualBlockIoPpiGuid
,
194 if (EFI_ERROR (Status
)) {
196 // Done with all Block Io Ppis
201 PeiServices
= (EFI_PEI_SERVICES
**)GetPeiServicesTablePointer ();
203 Status
= BlockIo2Ppi
->GetNumberOfBlockDevices (
209 Status
= BlockIoPpi
->GetNumberOfBlockDevices (
216 if (EFI_ERROR (Status
) || (NumberBlockDevices
== 0)) {
221 // Just retrieve the first block, should emulate all blocks.
223 for (IndexBlockDevice
= 1; IndexBlockDevice
<= NumberBlockDevices
&& PrivateData
->CapsuleCount
< PEI_CD_EXPRESS_MAX_CAPSULE_NUMBER
; IndexBlockDevice
++) {
225 Status
= BlockIo2Ppi
->GetBlockDeviceMediaInfo (
231 if (EFI_ERROR (Status
) ||
232 !Media2
.MediaPresent
||
233 ((Media2
.InterfaceType
!= MSG_ATAPI_DP
) && (Media2
.InterfaceType
!= MSG_USB_DP
)) ||
234 (Media2
.BlockSize
!= PEI_CD_BLOCK_SIZE
)
240 DEBUG ((DEBUG_INFO
, "PeiCdExpress InterfaceType is %d\n", Media2
.InterfaceType
));
241 DEBUG ((DEBUG_INFO
, "PeiCdExpress MediaPresent is %d\n", Media2
.MediaPresent
));
242 DEBUG ((DEBUG_INFO
, "PeiCdExpress BlockSize is 0x%x\n", Media2
.BlockSize
));
244 Status
= BlockIoPpi
->GetBlockDeviceMediaInfo (
250 if (EFI_ERROR (Status
) ||
251 !Media
.MediaPresent
||
252 ((Media
.DeviceType
!= IdeCDROM
) && (Media
.DeviceType
!= UsbMassStorage
)) ||
253 (Media
.BlockSize
!= PEI_CD_BLOCK_SIZE
)
259 DEBUG ((DEBUG_INFO
, "PeiCdExpress DeviceType is %d\n", Media
.DeviceType
));
260 DEBUG ((DEBUG_INFO
, "PeiCdExpress MediaPresent is %d\n", Media
.MediaPresent
));
261 DEBUG ((DEBUG_INFO
, "PeiCdExpress BlockSize is 0x%x\n", Media
.BlockSize
));
264 DEBUG ((DEBUG_INFO
, "PeiCdExpress Status is %d\n", Status
));
266 DEBUG ((DEBUG_INFO
, "IndexBlockDevice is %d\n", IndexBlockDevice
));
267 PrivateData
->CapsuleData
[PrivateData
->CapsuleCount
].IndexBlock
= IndexBlockDevice
;
269 PrivateData
->CapsuleData
[PrivateData
->CapsuleCount
].BlockIo2
= BlockIo2Ppi
;
271 PrivateData
->CapsuleData
[PrivateData
->CapsuleCount
].BlockIo
= BlockIoPpi
;
274 Status
= FindRecoveryCapsules (PrivateData
);
275 DEBUG ((DEBUG_INFO
, "Status is %d\n", Status
));
277 if (EFI_ERROR (Status
)) {
281 PrivateData
->CapsuleCount
++;
289 Finds out the recovery capsule in the current volume.
291 @param PrivateData The private data structure that contains recovery module information.
293 @retval EFI_SUCCESS The recovery capsule is successfully found in the volume.
294 @retval EFI_NOT_FOUND The recovery capsule is not found in the volume.
299 FindRecoveryCapsules (
300 IN OUT PEI_CD_EXPRESS_PRIVATE_DATA
*PrivateData
305 EFI_PEI_RECOVERY_BLOCK_IO_PPI
*BlockIoPpi
;
306 EFI_PEI_RECOVERY_BLOCK_IO2_PPI
*BlockIo2Ppi
;
312 PEI_CD_EXPRESS_DIR_FILE_RECORD
*RoorDirRecord
;
313 UINTN VolumeSpaceSize
;
314 BOOLEAN StartOfVolume
;
316 UINTN IndexBlockDevice
;
318 Buffer
= PrivateData
->BlockBuffer
;
319 BufferSize
= PEI_CD_BLOCK_SIZE
;
323 // The volume descriptor starts on Lba 16
325 IndexBlockDevice
= PrivateData
->CapsuleData
[PrivateData
->CapsuleCount
].IndexBlock
;
326 BlockIoPpi
= PrivateData
->CapsuleData
[PrivateData
->CapsuleCount
].BlockIo
;
327 BlockIo2Ppi
= PrivateData
->CapsuleData
[PrivateData
->CapsuleCount
].BlockIo2
;
330 StartOfVolume
= TRUE
;
334 SetMem (Buffer
, BufferSize
, 0);
335 if (BlockIo2Ppi
!= NULL
) {
336 Status
= BlockIo2Ppi
->ReadBlocks (
337 (EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (),
345 Status
= BlockIoPpi
->ReadBlocks (
346 (EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (),
355 if (EFI_ERROR (Status
)) {
359 StandardID
= (UINT8
*)(Buffer
+ PEI_CD_EXPRESS_STANDARD_ID_OFFSET
);
360 if (!StringCmp (StandardID
, (UINT8
*)PEI_CD_STANDARD_ID
, PEI_CD_EXPRESS_STANDARD_ID_SIZE
, TRUE
)) {
366 StartOfVolume
= FALSE
;
369 Type
= *(UINT8
*)(Buffer
+ PEI_CD_EXPRESS_VOLUME_TYPE_OFFSET
);
370 if (Type
== PEI_CD_EXPRESS_VOLUME_TYPE_TERMINATOR
) {
371 if (VolumeSpaceSize
== 0) {
374 Lba
= (OriginalLBA
+ VolumeSpaceSize
);
376 StartOfVolume
= TRUE
;
381 if (Type
!= PEI_CD_EXPRESS_VOLUME_TYPE_PRIMARY
) {
386 VolumeSpaceSize
= *(UINT32
*)(Buffer
+ PEI_CD_EXPRESS_VOLUME_SPACE_OFFSET
);
388 RoorDirRecord
= (PEI_CD_EXPRESS_DIR_FILE_RECORD
*)(Buffer
+ PEI_CD_EXPRESS_ROOT_DIR_RECORD_OFFSET
);
389 RootDirLBA
= RoorDirRecord
->LocationOfExtent
[0];
391 Status
= RetrieveCapsuleFileFromRoot (PrivateData
, BlockIoPpi
, BlockIo2Ppi
, IndexBlockDevice
, RootDirLBA
);
392 if (!EFI_ERROR (Status
)) {
394 // Just look for the first primary descriptor
402 return EFI_NOT_FOUND
;
406 Retrieves the recovery capsule in root directory of the current volume.
408 @param PrivateData The private data structure that contains recovery module information.
409 @param BlockIoPpi The Block IO PPI used to access the volume.
410 @param BlockIo2Ppi The Block IO 2 PPI used to access the volume.
411 @param IndexBlockDevice The index of current block device.
412 @param Lba The starting logic block address to retrieve capsule.
414 @retval EFI_SUCCESS The recovery capsule is successfully found in the volume.
415 @retval EFI_NOT_FOUND The recovery capsule is not found in the volume.
421 RetrieveCapsuleFileFromRoot (
422 IN OUT PEI_CD_EXPRESS_PRIVATE_DATA
*PrivateData
,
423 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI
*BlockIoPpi
,
424 IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI
*BlockIo2Ppi
,
425 IN UINTN IndexBlockDevice
,
432 PEI_CD_EXPRESS_DIR_FILE_RECORD
*FileRecord
;
435 Buffer
= PrivateData
->BlockBuffer
;
436 BufferSize
= PEI_CD_BLOCK_SIZE
;
438 SetMem (Buffer
, BufferSize
, 0);
440 if (BlockIo2Ppi
!= NULL
) {
441 Status
= BlockIo2Ppi
->ReadBlocks (
442 (EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (),
450 Status
= BlockIoPpi
->ReadBlocks (
451 (EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (),
460 if (EFI_ERROR (Status
)) {
465 FileRecord
= (PEI_CD_EXPRESS_DIR_FILE_RECORD
*)Buffer
;
467 if (FileRecord
->Length
== 0) {
472 // Not intend to check other flag now
474 if ((FileRecord
->Flag
& PEI_CD_EXPRESS_DIR_FILE_REC_FLAG_ISDIR
) != 0) {
475 Buffer
+= FileRecord
->Length
;
479 for (Index
= 0; Index
< FileRecord
->FileIDLength
; Index
++) {
480 if (FileRecord
->FileID
[Index
] == ';') {
485 if (Index
!= mRecoveryFileNameSize
- 1) {
486 Buffer
+= FileRecord
->Length
;
490 if (!StringCmp (FileRecord
->FileID
, (UINT8
*)mRecoveryFileName
, mRecoveryFileNameSize
- 1, FALSE
)) {
491 Buffer
+= FileRecord
->Length
;
495 PrivateData
->CapsuleData
[PrivateData
->CapsuleCount
].CapsuleStartLBA
= FileRecord
->LocationOfExtent
[0];
496 PrivateData
->CapsuleData
[PrivateData
->CapsuleCount
].CapsuleBlockAlignedSize
=
498 FileRecord
->DataLength
[0] /
503 PrivateData
->CapsuleData
[PrivateData
->CapsuleCount
].CapsuleSize
= FileRecord
->DataLength
[0];
508 return EFI_NOT_FOUND
;
512 Returns the number of DXE capsules residing on the device.
514 This function searches for DXE capsules from the associated device and returns
515 the number and maximum size in bytes of the capsules discovered. Entry 1 is
516 assumed to be the highest load priority and entry N is assumed to be the lowest
519 @param[in] PeiServices General-purpose services that are available
521 @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
523 @param[out] NumberRecoveryCapsules Pointer to a caller-allocated UINTN. On
524 output, *NumberRecoveryCapsules contains
525 the number of recovery capsule images
526 available for retrieval from this PEIM
529 @retval EFI_SUCCESS One or more capsules were discovered.
530 @retval EFI_DEVICE_ERROR A device error occurred.
531 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
536 GetNumberRecoveryCapsules (
537 IN EFI_PEI_SERVICES
**PeiServices
,
538 IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
*This
,
539 OUT UINTN
*NumberRecoveryCapsules
542 PEI_CD_EXPRESS_PRIVATE_DATA
*PrivateData
;
544 PrivateData
= PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS (This
);
545 UpdateBlocksAndVolumes (PrivateData
, TRUE
);
546 UpdateBlocksAndVolumes (PrivateData
, FALSE
);
547 *NumberRecoveryCapsules
= PrivateData
->CapsuleCount
;
549 if (*NumberRecoveryCapsules
== 0) {
550 return EFI_NOT_FOUND
;
557 Returns the size and type of the requested recovery capsule.
559 This function gets the size and type of the capsule specified by CapsuleInstance.
561 @param[in] PeiServices General-purpose services that are available to every PEIM
562 @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
564 @param[in] CapsuleInstance Specifies for which capsule instance to retrieve
565 the information. This parameter must be between
566 one and the value returned by GetNumberRecoveryCapsules()
567 in NumberRecoveryCapsules.
568 @param[out] Size A pointer to a caller-allocated UINTN in which
569 the size of the requested recovery module is
571 @param[out] CapsuleType A pointer to a caller-allocated EFI_GUID in which
572 the type of the requested recovery capsule is
573 returned. The semantic meaning of the value
574 returned is defined by the implementation.
576 @retval EFI_SUCCESS One or more capsules were discovered.
577 @retval EFI_DEVICE_ERROR A device error occurred.
578 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
583 GetRecoveryCapsuleInfo (
584 IN EFI_PEI_SERVICES
**PeiServices
,
585 IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
*This
,
586 IN UINTN CapsuleInstance
,
588 OUT EFI_GUID
*CapsuleType
591 PEI_CD_EXPRESS_PRIVATE_DATA
*PrivateData
;
592 UINTN NumberRecoveryCapsules
;
595 Status
= GetNumberRecoveryCapsules (PeiServices
, This
, &NumberRecoveryCapsules
);
597 if (EFI_ERROR (Status
)) {
601 if ((CapsuleInstance
== 0) || (CapsuleInstance
> NumberRecoveryCapsules
)) {
602 return EFI_NOT_FOUND
;
605 PrivateData
= PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS (This
);
607 *Size
= PrivateData
->CapsuleData
[CapsuleInstance
- 1].CapsuleSize
;
610 &gRecoveryOnDataCdGuid
,
618 Loads a DXE capsule from some media into memory.
620 This function, by whatever mechanism, retrieves a DXE capsule from some device
621 and loads it into memory. Note that the published interface is device neutral.
623 @param[in] PeiServices General-purpose services that are available
625 @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
627 @param[in] CapsuleInstance Specifies which capsule instance to retrieve.
628 @param[out] Buffer Specifies a caller-allocated buffer in which
629 the requested recovery capsule will be returned.
631 @retval EFI_SUCCESS The capsule was loaded correctly.
632 @retval EFI_DEVICE_ERROR A device error occurred.
633 @retval EFI_NOT_FOUND A requested recovery DXE capsule cannot be found.
638 LoadRecoveryCapsule (
639 IN EFI_PEI_SERVICES
**PeiServices
,
640 IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
*This
,
641 IN UINTN CapsuleInstance
,
646 PEI_CD_EXPRESS_PRIVATE_DATA
*PrivateData
;
647 EFI_PEI_RECOVERY_BLOCK_IO_PPI
*BlockIoPpi
;
648 EFI_PEI_RECOVERY_BLOCK_IO2_PPI
*BlockIo2Ppi
;
649 UINTN NumberRecoveryCapsules
;
651 Status
= GetNumberRecoveryCapsules (PeiServices
, This
, &NumberRecoveryCapsules
);
653 if (EFI_ERROR (Status
)) {
657 if ((CapsuleInstance
== 0) || (CapsuleInstance
> NumberRecoveryCapsules
)) {
658 return EFI_NOT_FOUND
;
661 PrivateData
= PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS (This
);
662 BlockIoPpi
= PrivateData
->CapsuleData
[CapsuleInstance
- 1].BlockIo
;
663 BlockIo2Ppi
= PrivateData
->CapsuleData
[CapsuleInstance
- 1].BlockIo2
;
665 if (BlockIo2Ppi
!= NULL
) {
666 Status
= BlockIo2Ppi
->ReadBlocks (
669 PrivateData
->CapsuleData
[CapsuleInstance
- 1].IndexBlock
,
670 PrivateData
->CapsuleData
[CapsuleInstance
- 1].CapsuleStartLBA
,
671 PrivateData
->CapsuleData
[CapsuleInstance
- 1].CapsuleBlockAlignedSize
,
675 Status
= BlockIoPpi
->ReadBlocks (
678 PrivateData
->CapsuleData
[CapsuleInstance
- 1].IndexBlock
,
679 PrivateData
->CapsuleData
[CapsuleInstance
- 1].CapsuleStartLBA
,
680 PrivateData
->CapsuleData
[CapsuleInstance
- 1].CapsuleBlockAlignedSize
,
689 This function compares two ASCII strings in case sensitive/insensitive way.
691 @param Source1 The first string.
692 @param Source2 The second string.
693 @param Size The maximum comparison length.
694 @param CaseSensitive Flag to indicate whether the comparison is case sensitive.
696 @retval TRUE The two strings are the same.
697 @retval FALSE The two string are not the same.
705 IN BOOLEAN CaseSensitive
711 for (Index
= 0; Index
< Size
; Index
++) {
712 if (Source1
[Index
] == Source2
[Index
]) {
716 if (!CaseSensitive
) {
717 Dif
= (UINT8
)((Source1
[Index
] > Source2
[Index
]) ? (Source1
[Index
] - Source2
[Index
]) : (Source2
[Index
] - Source1
[Index
]));
718 if (Dif
== ('a' - 'A')) {