/** @file | |
This file implements some PEI services about PPI. | |
Copyright (c) 2023, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "UnitTestPeiServicesTablePointerLib.h" | |
/** | |
This function installs an interface in the PEI PPI database by GUID. | |
The purpose of the service is to publish an interface that other parties | |
can use to call additional PEIMs. | |
@param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
@param PpiList Pointer to a list of PEI PPI Descriptors. | |
@param Single TRUE if only single entry in the PpiList. | |
FALSE if the PpiList is ended with an entry which has the | |
EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST flag set in its Flags field. | |
@retval EFI_SUCCESS if all PPIs in PpiList are successfully installed. | |
@retval EFI_INVALID_PARAMETER if PpiList is NULL pointer | |
if any PPI in PpiList is not valid | |
@retval EFI_OUT_OF_RESOURCES if there is no more memory resource to install PPI | |
**/ | |
EFI_STATUS | |
InternalPeiInstallPpi ( | |
IN CONST EFI_PEI_SERVICES **PeiServices, | |
IN CONST EFI_PEI_PPI_DESCRIPTOR *PpiList, | |
IN BOOLEAN Single | |
) | |
{ | |
PEI_CORE_INSTANCE *PrivateData; | |
PEI_PPI_LIST *PpiListPointer; | |
UINTN Index; | |
UINTN LastCount; | |
if (PpiList == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); | |
PpiListPointer = &PrivateData->PpiData.PpiList; | |
Index = PpiListPointer->CurrentCount; | |
LastCount = Index; | |
// | |
// This is loop installs all PPI descriptors in the PpiList. It is terminated | |
// by the EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST being set in the last | |
// EFI_PEI_PPI_DESCRIPTOR in the list. | |
// | |
for ( ; ;) { | |
// | |
// Check if it is a valid PPI. | |
// If not, rollback list to exclude all in this list. | |
// Try to indicate which item failed. | |
// | |
if ((PpiList->Flags & EFI_PEI_PPI_DESCRIPTOR_PPI) == 0) { | |
PpiListPointer->CurrentCount = LastCount; | |
DEBUG ((DEBUG_ERROR, "ERROR -> InstallPpi: %g %p\n", PpiList->Guid, PpiList->Ppi)); | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Index >= PpiListPointer->MaxCount) { | |
// | |
// Run out of room, assert. | |
// | |
ASSERT (Index < PpiListPointer->MaxCount); | |
} | |
DEBUG ((DEBUG_INFO, "Install PPI: %g\n", PpiList->Guid)); | |
PpiListPointer->PpiPtrs[Index].Ppi = (EFI_PEI_PPI_DESCRIPTOR *)PpiList; | |
Index++; | |
PpiListPointer->CurrentCount++; | |
if (Single) { | |
// | |
// Only single entry in the PpiList. | |
// | |
break; | |
} else if ((PpiList->Flags & EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) == | |
EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) | |
{ | |
// | |
// Continue until the end of the PPI List. | |
// | |
break; | |
} | |
// | |
// Go to the next descriptor. | |
// | |
PpiList++; | |
} | |
// | |
// Process any callback level notifies for newly installed PPIs. | |
// | |
ProcessNotify ( | |
PrivateData, | |
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK, | |
LastCount, | |
PpiListPointer->CurrentCount, | |
0, | |
PrivateData->PpiData.CallbackNotifyList.CurrentCount | |
); | |
return EFI_SUCCESS; | |
} | |
/** | |
This function installs an interface in the PEI PPI database by GUID. | |
The purpose of the service is to publish an interface that other parties | |
can use to call additional PEIMs. | |
@param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
@param PpiList Pointer to a list of PEI PPI Descriptors. | |
@retval EFI_SUCCESS if all PPIs in PpiList are successfully installed. | |
@retval EFI_INVALID_PARAMETER if PpiList is NULL pointer | |
if any PPI in PpiList is not valid | |
@retval EFI_OUT_OF_RESOURCES if there is no more memory resource to install PPI | |
**/ | |
EFI_STATUS | |
EFIAPI | |
UnitTestInstallPpi ( | |
IN CONST EFI_PEI_SERVICES **PeiServices, | |
IN CONST EFI_PEI_PPI_DESCRIPTOR *PpiList | |
) | |
{ | |
return InternalPeiInstallPpi (PeiServices, PpiList, FALSE); | |
} | |
/** | |
This function reinstalls an interface in the PEI PPI database by GUID. | |
The purpose of the service is to publish an interface that other parties can | |
use to replace an interface of the same name in the protocol database with a | |
different interface. | |
@param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
@param OldPpi Pointer to the old PEI PPI Descriptors. | |
@param NewPpi Pointer to the new PEI PPI Descriptors. | |
@retval EFI_SUCCESS if the operation was successful | |
@retval EFI_INVALID_PARAMETER if OldPpi or NewPpi is NULL | |
@retval EFI_INVALID_PARAMETER if NewPpi is not valid | |
@retval EFI_NOT_FOUND if the PPI was not in the database | |
**/ | |
EFI_STATUS | |
EFIAPI | |
UnitTestReInstallPpi ( | |
IN CONST EFI_PEI_SERVICES **PeiServices, | |
IN CONST EFI_PEI_PPI_DESCRIPTOR *OldPpi, | |
IN CONST EFI_PEI_PPI_DESCRIPTOR *NewPpi | |
) | |
{ | |
PEI_CORE_INSTANCE *PrivateData; | |
UINTN Index; | |
if ((OldPpi == NULL) || (NewPpi == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((NewPpi->Flags & EFI_PEI_PPI_DESCRIPTOR_PPI) == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); | |
// | |
// Find the old PPI instance in the database. If we can not find it, | |
// return the EFI_NOT_FOUND error. | |
// | |
for (Index = 0; Index < PrivateData->PpiData.PpiList.CurrentCount; Index++) { | |
if (OldPpi == PrivateData->PpiData.PpiList.PpiPtrs[Index].Ppi) { | |
break; | |
} | |
} | |
if (Index == PrivateData->PpiData.PpiList.CurrentCount) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Replace the old PPI with the new one. | |
// | |
DEBUG ((DEBUG_INFO, "Reinstall PPI: %g\n", NewPpi->Guid)); | |
PrivateData->PpiData.PpiList.PpiPtrs[Index].Ppi = (EFI_PEI_PPI_DESCRIPTOR *)NewPpi; | |
// | |
// Process any callback level notifies for the newly installed PPI. | |
// | |
ProcessNotify ( | |
PrivateData, | |
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK, | |
Index, | |
Index+1, | |
0, | |
PrivateData->PpiData.CallbackNotifyList.CurrentCount | |
); | |
return EFI_SUCCESS; | |
} | |
/** | |
Locate a given named PPI. | |
@param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
@param Guid Pointer to GUID of the PPI. | |
@param Instance Instance Number to discover. | |
@param PpiDescriptor Pointer to reference the found descriptor. If not NULL, | |
returns a pointer to the descriptor (includes flags, etc) | |
@param Ppi Pointer to reference the found PPI | |
@retval EFI_SUCCESS if the PPI is in the database | |
@retval EFI_NOT_FOUND if the PPI is not in the database | |
**/ | |
EFI_STATUS | |
EFIAPI | |
UnitTestLocatePpi ( | |
IN CONST EFI_PEI_SERVICES **PeiServices, | |
IN CONST EFI_GUID *Guid, | |
IN UINTN Instance, | |
IN OUT EFI_PEI_PPI_DESCRIPTOR **PpiDescriptor, | |
IN OUT VOID **Ppi | |
) | |
{ | |
PEI_CORE_INSTANCE *PrivateData; | |
UINTN Index; | |
EFI_GUID *CheckGuid; | |
EFI_PEI_PPI_DESCRIPTOR *TempPtr; | |
PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); | |
// | |
// Search the data base for the matching instance of the GUIDed PPI. | |
// | |
for (Index = 0; Index < PrivateData->PpiData.PpiList.CurrentCount; Index++) { | |
TempPtr = PrivateData->PpiData.PpiList.PpiPtrs[Index].Ppi; | |
CheckGuid = TempPtr->Guid; | |
// | |
// Don't use CompareGuid function here for performance reasons. | |
// Instead we compare the GUID as INT32 at a time and branch | |
// on the first failed comparison. | |
// | |
if ((((INT32 *)Guid)[0] == ((INT32 *)CheckGuid)[0]) && | |
(((INT32 *)Guid)[1] == ((INT32 *)CheckGuid)[1]) && | |
(((INT32 *)Guid)[2] == ((INT32 *)CheckGuid)[2]) && | |
(((INT32 *)Guid)[3] == ((INT32 *)CheckGuid)[3])) | |
{ | |
if (Instance == 0) { | |
if (PpiDescriptor != NULL) { | |
*PpiDescriptor = TempPtr; | |
} | |
if (Ppi != NULL) { | |
*Ppi = TempPtr->Ppi; | |
} | |
return EFI_SUCCESS; | |
} | |
Instance--; | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
This function installs a notification service to be called back when a given | |
interface is installed or reinstalled. The purpose of the service is to publish | |
an interface that other parties can use to call additional PPIs that may materialize later. | |
@param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
@param NotifyList Pointer to list of Descriptors to notify upon. | |
@param Single TRUE if only single entry in the NotifyList. | |
FALSE if the NotifyList is ended with an entry which has the | |
EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST flag set in its Flags field. | |
@retval EFI_SUCCESS if successful | |
@retval EFI_OUT_OF_RESOURCES if no space in the database | |
@retval EFI_INVALID_PARAMETER if not a good descriptor | |
**/ | |
EFI_STATUS | |
InternalPeiNotifyPpi ( | |
IN CONST EFI_PEI_SERVICES **PeiServices, | |
IN CONST EFI_PEI_NOTIFY_DESCRIPTOR *NotifyList, | |
IN BOOLEAN Single | |
) | |
{ | |
PEI_CORE_INSTANCE *PrivateData; | |
PEI_CALLBACK_NOTIFY_LIST *CallbackNotifyListPointer; | |
UINTN CallbackNotifyIndex; | |
UINTN LastCallbackNotifyCount; | |
PEI_DISPATCH_NOTIFY_LIST *DispatchNotifyListPointer; | |
UINTN DispatchNotifyIndex; | |
UINTN LastDispatchNotifyCount; | |
if (NotifyList == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); | |
CallbackNotifyListPointer = &PrivateData->PpiData.CallbackNotifyList; | |
CallbackNotifyIndex = CallbackNotifyListPointer->CurrentCount; | |
LastCallbackNotifyCount = CallbackNotifyIndex; | |
DispatchNotifyListPointer = &PrivateData->PpiData.DispatchNotifyList; | |
DispatchNotifyIndex = DispatchNotifyListPointer->CurrentCount; | |
LastDispatchNotifyCount = DispatchNotifyIndex; | |
// | |
// This is loop installs all Notify descriptors in the NotifyList. It is | |
// terminated by the EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST being set in the last | |
// EFI_PEI_NOTIFY_DESCRIPTOR in the list. | |
// | |
for ( ; ;) { | |
// | |
// If some of the PPI data is invalid restore original Notify PPI database value | |
// | |
if ((NotifyList->Flags & EFI_PEI_PPI_DESCRIPTOR_NOTIFY_TYPES) == 0) { | |
CallbackNotifyListPointer->CurrentCount = LastCallbackNotifyCount; | |
DispatchNotifyListPointer->CurrentCount = LastDispatchNotifyCount; | |
DEBUG ((DEBUG_ERROR, "ERROR -> NotifyPpi: %g %p\n", NotifyList->Guid, NotifyList->Notify)); | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((NotifyList->Flags & EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK) != 0) { | |
if (CallbackNotifyIndex >= CallbackNotifyListPointer->MaxCount) { | |
// | |
// Run out of room, assert. | |
// | |
ASSERT (CallbackNotifyIndex < CallbackNotifyListPointer->MaxCount); | |
} | |
CallbackNotifyListPointer->NotifyPtrs[CallbackNotifyIndex].Notify = (EFI_PEI_NOTIFY_DESCRIPTOR *)NotifyList; | |
CallbackNotifyIndex++; | |
CallbackNotifyListPointer->CurrentCount++; | |
} else { | |
ASSERT ((NotifyList->Flags & EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK) != 0); | |
} | |
DEBUG ((DEBUG_INFO, "Register PPI Notify: %g\n", NotifyList->Guid)); | |
if (Single) { | |
// | |
// Only single entry in the NotifyList. | |
// | |
break; | |
} else if ((NotifyList->Flags & EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) == | |
EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) | |
{ | |
// | |
// Continue until the end of the Notify List. | |
// | |
break; | |
} | |
// | |
// Go to the next descriptor. | |
// | |
NotifyList++; | |
} | |
// | |
// Process any callback level notifies for all previously installed PPIs. | |
// | |
ProcessNotify ( | |
PrivateData, | |
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK, | |
0, | |
PrivateData->PpiData.PpiList.CurrentCount, | |
LastCallbackNotifyCount, | |
CallbackNotifyListPointer->CurrentCount | |
); | |
return EFI_SUCCESS; | |
} | |
/** | |
This function installs a notification service to be called back when a given | |
interface is installed or reinstalled. The purpose of the service is to publish | |
an interface that other parties can use to call additional PPIs that may materialize later. | |
@param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
@param NotifyList Pointer to list of Descriptors to notify upon. | |
@retval EFI_SUCCESS if successful | |
@retval EFI_OUT_OF_RESOURCES if no space in the database | |
@retval EFI_INVALID_PARAMETER if not a good descriptor | |
**/ | |
EFI_STATUS | |
EFIAPI | |
UnitTestNotifyPpi ( | |
IN CONST EFI_PEI_SERVICES **PeiServices, | |
IN CONST EFI_PEI_NOTIFY_DESCRIPTOR *NotifyList | |
) | |
{ | |
return InternalPeiNotifyPpi (PeiServices, NotifyList, FALSE); | |
} | |
/** | |
Process notifications. | |
@param PrivateData PeiCore's private data structure | |
@param NotifyType Type of notify to fire. | |
@param InstallStartIndex Install Beginning index. | |
@param InstallStopIndex Install Ending index. | |
@param NotifyStartIndex Notify Beginning index. | |
@param NotifyStopIndex Notify Ending index. | |
**/ | |
VOID | |
ProcessNotify ( | |
IN PEI_CORE_INSTANCE *PrivateData, | |
IN UINTN NotifyType, | |
IN INTN InstallStartIndex, | |
IN INTN InstallStopIndex, | |
IN INTN NotifyStartIndex, | |
IN INTN NotifyStopIndex | |
) | |
{ | |
INTN Index1; | |
INTN Index2; | |
EFI_GUID *SearchGuid; | |
EFI_GUID *CheckGuid; | |
EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor; | |
for (Index1 = NotifyStartIndex; Index1 < NotifyStopIndex; Index1++) { | |
if (NotifyType == EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK) { | |
NotifyDescriptor = PrivateData->PpiData.CallbackNotifyList.NotifyPtrs[Index1].Notify; | |
} else { | |
NotifyDescriptor = PrivateData->PpiData.DispatchNotifyList.NotifyPtrs[Index1].Notify; | |
} | |
CheckGuid = NotifyDescriptor->Guid; | |
for (Index2 = InstallStartIndex; Index2 < InstallStopIndex; Index2++) { | |
SearchGuid = PrivateData->PpiData.PpiList.PpiPtrs[Index2].Ppi->Guid; | |
// | |
// Don't use CompareGuid function here for performance reasons. | |
// Instead we compare the GUID as INT32 at a time and branch | |
// on the first failed comparison. | |
// | |
if ((((INT32 *)SearchGuid)[0] == ((INT32 *)CheckGuid)[0]) && | |
(((INT32 *)SearchGuid)[1] == ((INT32 *)CheckGuid)[1]) && | |
(((INT32 *)SearchGuid)[2] == ((INT32 *)CheckGuid)[2]) && | |
(((INT32 *)SearchGuid)[3] == ((INT32 *)CheckGuid)[3])) | |
{ | |
DEBUG (( | |
DEBUG_INFO, | |
"Notify: PPI Guid: %g, Peim notify entry point: %p\n", | |
SearchGuid, | |
NotifyDescriptor->Notify | |
)); | |
NotifyDescriptor->Notify ( | |
(EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), | |
NotifyDescriptor, | |
(PrivateData->PpiData.PpiList.PpiPtrs[Index2].Ppi)->Ppi | |
); | |
} | |
} | |
} | |
} |