diff --git a/.pytool/CISettings.py b/.pytool/CISettings.py index 314758da32e..3115780e066 100644 --- a/.pytool/CISettings.py +++ b/.pytool/CISettings.py @@ -86,6 +86,7 @@ def GetPackagesSupported(self): "MdeModulePkg", "NetworkPkg", "PcAtChipsetPkg", + "PolicyServicePkg", "SecurityPkg", "UefiCpuPkg", "FmpDevicePkg", diff --git a/PolicyServicePkg/Include/Library/PolicyLib.h b/PolicyServicePkg/Include/Library/PolicyLib.h new file mode 100644 index 00000000000..fbc920d9742 --- /dev/null +++ b/PolicyServicePkg/Include/Library/PolicyLib.h @@ -0,0 +1,234 @@ +/** @file + Definitions for the policy libraries. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _POLICY_LIB_H_ +#define _POLICY_LIB_H_ + +#include + +#define VERIFIED_POLICY_LIB_VERSION 1 + +#pragma pack(1) + +typedef struct _VERIFIED_POLICY_HEADER { + UINT64 Signature; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT32 Size; +} VERIFIED_POLICY_HEADER; + +#pragma pack() + +typedef VERIFIED_POLICY_HEADER VERIFIED_POLICY_DESCRIPTOR; + +/** + Creates or updates a policy in the policy store. Will notify any applicable + callbacks. + + @param[in] PolicyGuid The uniquely identifying GUID for the policy. + @param[in] Attributes Attributes of the policy to be set. + @param[in] Policy The policy data buffer. This buffer will be + copied into the data store. + @param[in] PolicySize The size of the provided policy data. + + @retval EFI_SUCCESS Policy was created or updated. + @retval EFI_ACCESS_DENIED Policy was already finalized prior to this call. + @retval EFI_OUT_OF_RESOURCES Failed to allocate space for policy structures. +**/ +EFI_STATUS +EFIAPI +SetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT64 Attributes, + IN VOID *Policy, + IN UINT16 PolicySize + ); + +/** + Retrieves the policy descriptor, buffer, and size for a given policy GUID. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + @param[out] Attributes The attributes of the stored policy. + @param[out] Policy The buffer where the policy data is copied. + @param[in,out] PolicySize The size of the stored policy data buffer. + On output, contains the size of the stored policy. + + @retval EFI_SUCCESS The policy was retrieved. + @retval EFI_BUFFER_TOO_SMALL The provided buffer size was too small. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +GetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + OUT UINT64 *Attributes OPTIONAL, + OUT VOID *Policy, + IN OUT UINT16 *PolicySize + ); + +/** + Removes a policy from the policy store. The policy will be removed from the store + and freed if possible. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + + @retval EFI_SUCCESS The policy was removed. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +RemovePolicy ( + IN CONST EFI_GUID *PolicyGuid + ); + +/** + Registers a callback for a policy event notification. The provided routine + will be invoked when one of multiple of the provided event types for the specified + guid occurs. + + @param[in] PolicyGuid The GUID of the policy the being watched. + @param[in] EventTypes The events to notify the callback for. + @param[in] Priority The priority of the callback where the lower values + will be called first. + @param[in] CallbackRoutine The function pointer of the callback to be invoked. + @param[out] Handle Returns the handle to this callback entry. + + @retval EFI_SUCCESS The callback notification as successfully registered. + @retval EFI_INVALID_PARAMETER EventTypes was 0 or Callback routine is invalid. + @retval Other The callback registration failed. +**/ +EFI_STATUS +EFIAPI +RegisterPolicyNotify ( + IN CONST EFI_GUID *PolicyGuid, + IN CONST UINT32 EventTypes, + IN CONST UINT32 Priority, + IN POLICY_HANDLER_CALLBACK CallbackRoutine, + OUT VOID **Handle + ); + +/** + Removes a registered notification callback. + + @param[in] Handle The handle for the registered callback. + + @retval EFI_SUCCESS The callback notification as successfully removed. + @retval EFI_INVALID_PARAMETER The provided handle is invalid. + @retval EFI_NOT_FOUND The provided handle could not be found. +**/ +EFI_STATUS +EFIAPI +UnregisterPolicyNotify ( + IN VOID *Handle + ); + +/** + Retrieves a verified policy of the given type from the policy store. + + @param[in] PolicyGuid The GUID of policy in the policy store. + + @param[in] Descriptor The descriptor for the verified policy data + structure. + + @param[out] Attributes Returns the attributes of the policy in the + policy store. + + @param[out] DataHandle Returns the handle to the verified policy. + + @retval EFI_SUCCESS The policy was successfully retrieved. + @retval EFI_BAD_BUFFER_SIZE The policy was an unexpected size. + @retval EFI_INCOMPATIBLE_VERSION The verified policy major version did not + match. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +RETURN_STATUS +EFIAPI +GetVerifiedPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN CONST VERIFIED_POLICY_DESCRIPTOR *Descriptor, + OUT UINT64 *Attributes OPTIONAL, + OUT EFI_HANDLE *DataHandle + ); + +/** + Creates a new verified policy data structure. + + @param[in] Descriptor The descriptor of the verified policy data structure + to be created. + + @param[out] DataHandle The handle to the newly created verified policy data + structure. + + @retval EFI_SUCCESS The data structure was successfully created. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +RETURN_STATUS +EFIAPI +CreateVerifiedPolicy ( + IN CONST VERIFIED_POLICY_DESCRIPTOR *Descriptor, + OUT EFI_HANDLE *DataHandle + ); + +/** + Write a verified policy to the policy store. + + @param[in] PolicyGuid The GUID of policy in the policy store. + + @param[in] Attributes The attributes to set in the policy store. + + @param[in] DataHandle The handle to the policy data. + + @retval EFI_SUCCESS The policy was successfully retrieved. + @retval EFI_INVALID_PARAMETER DataHandle is NULL. + @retval EFI_BAD_BUFFER_SIZE The policy is too large. +**/ +RETURN_STATUS +EFIAPI +SetVerifiedPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT64 Attributes, + IN EFI_HANDLE DataHandle + ); + +/** + Closes a policy data handle. + + @param[in] DataHandle The policy handle to be closed. + + @retval EFI_SUCCESS The policy handle was successfully + closed. + @retval EFI_INVALID_PARAMETER The data handle is NULL. +**/ +RETURN_STATUS +EFIAPI +CloseVerifiedPolicy ( + IN EFI_HANDLE DataHandle + ); + +/** + Records access to a policy data structure used by autogenerated code. This + function should not be manually called. + + @param[in] DataHandle The policy handle where the access was made. + @param[in] CallerGuid The file guid of the caller for tracking access. + @param[in] Offset The offset into the policy data for the access. + @param[in] Size The size of the access. + @param[in] Write Indicates if the policy was written to. + +**/ +VOID +EFIAPI +ReportVerifiedPolicyAccess ( + IN EFI_HANDLE DataHandle, + IN CONST EFI_GUID *CallerGuid, + IN UINT32 Offset, + IN UINT32 Size, + IN BOOLEAN Write + ); + +#endif diff --git a/PolicyServicePkg/Include/PolicyInterface.h b/PolicyServicePkg/Include/PolicyInterface.h new file mode 100644 index 00000000000..7e837b66ca8 --- /dev/null +++ b/PolicyServicePkg/Include/PolicyInterface.h @@ -0,0 +1,159 @@ +/** @file + Common public header definitions for the policy interface. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _POLICY_INTERFACE_H_ +#define _POLICY_INTERFACE_H_ + +// Flag indicating the policy is not mutable. +#define POLICY_ATTRIBUTE_FINALIZED BIT0 + +// Indicating the provided policy should not be available in DXE. +#define POLICY_ATTRIBUTE_PEI_ONLY BIT1 + +/** + Creates or updates a policy in the policy store. Will notify any applicable + callbacks. + + @param[in] PolicyGuid The uniquely identifying GUID for the policy. + @param[in] Attributes Attributes of the policy to be set. + @param[in] Policy The policy data buffer. This buffer will be + copied into the data store. + @param[in] PolicySize The size of the provided policy data. + + @retval EFI_SUCCESS Policy was created or updated. + @retval EFI_ACCESS_DENIED Policy was already finalized prior to this call. + @retval EFI_OUT_OF_RESOURCES Failed to allocate space for policy structures. +**/ +typedef +EFI_STATUS +(EFIAPI *POLICY_SET_POLICY)( + IN CONST EFI_GUID *PolicyGuid, + IN UINT64 Attributes, + IN VOID *Policy, + IN UINT16 PolicySize + ); + +/** + Retrieves the policy descriptor, buffer, and size for a given policy GUID. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + @param[out] Attributes The attributes of the stored policy. + @param[out] Policy The buffer where the policy data is copied. + @param[in,out] PolicySize The size of the stored policy data buffer. + On output, contains the size of the stored policy. + + @retval EFI_SUCCESS The policy was retrieved. + @retval EFI_BUFFER_TOO_SMALL The provided buffer size was too small. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +typedef +EFI_STATUS +(EFIAPI *POLICY_GET_POLICY)( + IN CONST EFI_GUID *PolicyGuid, + OUT UINT64 *Attributes OPTIONAL, + OUT VOID *Policy, + IN OUT UINT16 *PolicySize + ); + +/** + Removes a policy from the policy store. The policy will be removed from the store + and freed if possible. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + + @retval EFI_SUCCESS The policy was removed. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +typedef +EFI_STATUS +(EFIAPI *POLICY_REMOVE_POLICY)( + IN CONST EFI_GUID *PolicyGuid + ); + +/** + Callback for a policy notification event. + + @param[in] PolicyGuid The GUID of the policy being notified. + @param[in] EventTypes The events that occurred for the notification. + @param[in] CallbackHandle The handle for the callback being invoked. +**/ +typedef +VOID +(EFIAPI *POLICY_HANDLER_CALLBACK)( + IN CONST EFI_GUID *PolicyGuid, + IN UINT32 EventTypes, + IN VOID *CallbackHandle + ); + +/** + Flags used for policy callbacks. + + POLICY_NOTIFY_SET - The policy content and/or attributes were set. + POLICY_NOTIFY_FINALIZED - The policy was set with the POLICY_ATTRIBUTE_FINALIZED set. + POLICY_NOTIFY_REMOVED - The policy was removed. + +**/ +#define POLICY_NOTIFY_SET (BIT0) +#define POLICY_NOTIFY_FINALIZED (BIT1) +#define POLICY_NOTIFY_REMOVED (BIT2) +#define POLICY_NOTIFY_ALL (POLICY_NOTIFY_SET | \ + POLICY_NOTIFY_FINALIZED | \ + POLICY_NOTIFY_REMOVED) + +#define POLICY_NOTIFY_DEFAULT_PRIORITY (512) + +/** + Registers a callback for a policy event notification. The provided routine + will be invoked when one of multiple of the provided event types for the specified + guid occurs. + + @param[in] PolicyGuid The GUID of the policy the being watched. + @param[in] EventTypes The events to notify the callback for. + @param[in] Priority The priority of the callback where the lower values + will be called first. + @param[in] CallbackRoutine The function pointer of the callback to be invoked. + @param[out] Handle Returns the handle to this callback entry. + + @retval EFI_SUCCESS The callback notification as successfully registered. + @retval EFI_INVALID_PARAMETER EventTypes was 0 or Callback routine is invalid. + @retval Other The callback registration failed. +**/ +typedef +EFI_STATUS +(EFIAPI *POLICY_REGISTER_CALLBACK)( + IN CONST EFI_GUID *PolicyGuid, + IN CONST UINT32 EventTypes, + IN CONST UINT32 Priority, + IN POLICY_HANDLER_CALLBACK CallbackRoutine, + OUT VOID **Handle + ); + +/** + Removes a registered notification callback. + + @param[in] Handle The handle for the registered callback. + + @retval EFI_SUCCESS The callback notification as successfully removed. + @retval EFI_INVALID_PARAMETER The provided handle is invalid. + @retval EFI_NOT_FOUND The provided handle could not be found. +**/ +typedef +EFI_STATUS +(EFIAPI *POLICY_UNREGISTER_CALLBACK)( + IN VOID *Handle + ); + +typedef struct _POLICY_INTERFACE { + POLICY_SET_POLICY SetPolicy; + POLICY_GET_POLICY GetPolicy; + POLICY_REMOVE_POLICY RemovePolicy; + POLICY_REGISTER_CALLBACK RegisterNotify; + POLICY_UNREGISTER_CALLBACK UnregisterNotify; +} POLICY_INTERFACE; + +#endif diff --git a/PolicyServicePkg/Include/Ppi/Policy.h b/PolicyServicePkg/Include/Ppi/Policy.h new file mode 100644 index 00000000000..c6e726a9dbe --- /dev/null +++ b/PolicyServicePkg/Include/Ppi/Policy.h @@ -0,0 +1,21 @@ +/** @file + This PPI provides services to publish, update, and retrieve general policies in the PEI + environment. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _POLICY_PPI_H_ +#define _POLICY_PPI_H_ + +#include + +#define POLICY_PPI_GUID {0xa8b33630, 0xa1ae, 0x4e2d, { 0x8d, 0x0f, 0x3d, 0xf3, 0xe5, 0x87, 0x08, 0xce } } + +typedef struct _POLICY_INTERFACE POLICY_PPI; + +extern EFI_GUID gPeiPolicyPpiGuid; + +#endif diff --git a/PolicyServicePkg/Include/Protocol/MmPolicy.h b/PolicyServicePkg/Include/Protocol/MmPolicy.h new file mode 100644 index 00000000000..9fcc1066cb0 --- /dev/null +++ b/PolicyServicePkg/Include/Protocol/MmPolicy.h @@ -0,0 +1,21 @@ +/** @file + This protocol provides services to publish, update, and retrieve general policies in the MM + environment. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MM_POLICY_PROTOCOL_H_ +#define _MM_POLICY_PROTOCOL_H_ + +#include + +#define MM_POLICY_PROTOCOL_GUID { 0xe55ad3a1, 0xbd34, 0x46f4, { 0xbb, 0x6e, 0x72, 0x28, 0x0b, 0xdc, 0xbf, 0xd9 } } + +typedef struct _POLICY_INTERFACE MM_POLICY_PROTOCOL; + +extern EFI_GUID gMmPolicyProtocolGuid; + +#endif diff --git a/PolicyServicePkg/Include/Protocol/Policy.h b/PolicyServicePkg/Include/Protocol/Policy.h new file mode 100644 index 00000000000..ece193d9f19 --- /dev/null +++ b/PolicyServicePkg/Include/Protocol/Policy.h @@ -0,0 +1,21 @@ +/** @file + This protocol provides services to publish, update, and retrieve general policies in the DXE + environment. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _POLICY_PROTOCOL_H_ +#define _POLICY_PROTOCOL_H_ + +#include + +#define POLICY_PROTOCOL_GUID {0xd7c9b744, 0x13a5, 0x4377, { 0x8d, 0x2a, 0x6b, 0x37, 0xad, 0x1f, 0xd8, 0x2a } } + +typedef struct _POLICY_INTERFACE POLICY_PROTOCOL; + +extern EFI_GUID gPolicyProtocolGuid; + +#endif diff --git a/PolicyServicePkg/Library/DxePolicyLib/DxePolicy.c b/PolicyServicePkg/Library/DxePolicyLib/DxePolicy.c new file mode 100644 index 00000000000..f00a7e70432 --- /dev/null +++ b/PolicyServicePkg/Library/DxePolicyLib/DxePolicy.c @@ -0,0 +1,135 @@ +/** @file + Implementation of the Verified Policy library for DXE. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include +#include + +#include "../PolicyLibCommon.h" + +STATIC EFI_EVENT mPolicyExitBootServicesEvent; +STATIC BOOLEAN mPolicyAtRuntime = FALSE; + +/** + A private helper function to retrieve the policy service protocol. + + @param[out] PolicyInterface Returns with the pointer to the protocol. + + @retval EFI_SUCCESS Policy protocol was found. + @retval EFI_NOT_FOUND Policy protocol was not found. + @retval EFI_UNSUPPORTED Policy service was called at runtime. +**/ +EFI_STATUS +GetPolicyInterface ( + OUT POLICY_INTERFACE **PolicyInterface + ) +{ + EFI_STATUS Status; + STATIC POLICY_PROTOCOL *mPolicyProtocol = NULL; + + if (mPolicyAtRuntime) { + mPolicyProtocol = NULL; + return EFI_UNSUPPORTED; + } + + Status = EFI_SUCCESS; + if (mPolicyProtocol == NULL) { + Status = gBS->LocateProtocol ( + &gPolicyProtocolGuid, + NULL, + (VOID **)&mPolicyProtocol + ); + + if (EFI_ERROR (Status)) { + mPolicyProtocol = NULL; + } + } + + if (mPolicyProtocol != NULL) { + *PolicyInterface = mPolicyProtocol; + } + + return Status; +} + +/** + Set AtRuntime flag as TRUE after ExitBootServices. + + @param[in] Event The Event that is being processed. + @param[in] Context The Event Context. + +**/ +VOID +EFIAPI +RuntimeLibExitBootServicesEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + mPolicyAtRuntime = TRUE; +} + +/** + This constructor sets up a callback for ExitBootServices to ensure policy + service is not used at runtime. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS Successfully initialized the policy library. + @retval Other Error returned by a subroutine. + +**/ +EFI_STATUS +EFIAPI +PolicyLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_NOTIFY, + RuntimeLibExitBootServicesEvent, + NULL, + &mPolicyExitBootServicesEvent + ); + + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This destructor closes the exit boot services event. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS Successfully initialized the policy library. + @retval Other Error returned by a subroutine. + +**/ +EFI_STATUS +EFIAPI +PolicyLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = gBS->CloseEvent (mPolicyExitBootServicesEvent); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/PolicyServicePkg/Library/DxePolicyLib/DxePolicyLib.inf b/PolicyServicePkg/Library/DxePolicyLib/DxePolicyLib.inf new file mode 100644 index 00000000000..9514705eccc --- /dev/null +++ b/PolicyServicePkg/Library/DxePolicyLib/DxePolicyLib.inf @@ -0,0 +1,38 @@ +## @file +# DXE instance of verified policy library. +# +# Copyright (c) Microsoft Corporation. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 1.26 + BASE_NAME = DxePolicyLib + MODULE_UNI_FILE = DxePolicyLib.uni + FILE_GUID = 66BE3195-F6D0-49D5-9C47-D3D7A6F7CC26 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = PolicyLib | DXE_DRIVER UEFI_DRIVER UEFI_APPLICATION DXE_RUNTIME_DRIVER + CONSTRUCTOR = PolicyLibConstructor + DESTRUCTOR = PolicyLibDestructor + +[Sources] + ../PolicyLibCommon.c + ../PolicyLibCommon.h + DxePolicy.c + +[Packages] + MdePkg/MdePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + UefiBootServicesTableLib + +[Protocols] + gPolicyProtocolGuid ## CONSUMES + +[Pcd] diff --git a/PolicyServicePkg/Library/DxePolicyLib/DxePolicyLib.uni b/PolicyServicePkg/Library/DxePolicyLib/DxePolicyLib.uni new file mode 100644 index 00000000000..02baa8bad0c --- /dev/null +++ b/PolicyServicePkg/Library/DxePolicyLib/DxePolicyLib.uni @@ -0,0 +1,14 @@ +// /** @file +// DXE Instance of Policy Library +// +// Copyright (c) Microsoft Corporation +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "DXE Instance of Policy Library" + +#string STR_MODULE_DESCRIPTION #language en-US "Accesses the policy service and validates policy data" + diff --git a/PolicyServicePkg/Library/MmPolicyLib/MmPolicy.c b/PolicyServicePkg/Library/MmPolicyLib/MmPolicy.c new file mode 100644 index 00000000000..7663f21b130 --- /dev/null +++ b/PolicyServicePkg/Library/MmPolicyLib/MmPolicy.c @@ -0,0 +1,53 @@ +/** @file + Implementation of the Verified Policy library for MM. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include +#include + +#include "../PolicyLibCommon.h" + +/** + A private helper function to retrieve the policy service protocol. + + @param[out] PolicyInterface Returns with the pointer to the protocol. + + @retval EFI_SUCCESS Policy protocol was found. + @retval EFI_NOT_FOUND Policy protocol was not found. +**/ +EFI_STATUS +GetPolicyInterface ( + OUT POLICY_INTERFACE **PolicyInterface + ) +{ + EFI_STATUS Status; + STATIC MM_POLICY_PROTOCOL *mPolicyProtocol = NULL; + + Status = EFI_SUCCESS; + if (mPolicyProtocol == NULL) { + Status = gMmst->MmLocateProtocol ( + &gMmPolicyProtocolGuid, + NULL, + (VOID **)&mPolicyProtocol + ); + + if (EFI_ERROR (Status)) { + mPolicyProtocol = NULL; + } + } + + if (mPolicyProtocol != NULL) { + *PolicyInterface = mPolicyProtocol; + } + + return Status; +} diff --git a/PolicyServicePkg/Library/MmPolicyLib/MmPolicyLib.inf b/PolicyServicePkg/Library/MmPolicyLib/MmPolicyLib.inf new file mode 100644 index 00000000000..2d355671ee8 --- /dev/null +++ b/PolicyServicePkg/Library/MmPolicyLib/MmPolicyLib.inf @@ -0,0 +1,36 @@ +## @file +# MM instance of verified policy library. +# +# Copyright (c) Microsoft Corporation. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 1.26 + PI_SPECIFICATION_VERSION = 0x00010032 + BASE_NAME = MmPolicyLib + FILE_GUID = C2A9C781-8D58-46DA-BC39-5385AB8D5C8A + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + LIBRARY_CLASS = PolicyLib | MM_STANDALONE + +[Sources] + ../PolicyLibCommon.c + ../PolicyLibCommon.h + MmPolicy.c + +[Packages] + MdePkg/MdePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + MmServicesTableLib + +[Protocols] + gMmPolicyProtocolGuid ## CONSUMES + +[Pcd] diff --git a/PolicyServicePkg/Library/PeiPolicyLib/PeiPolicy.c b/PolicyServicePkg/Library/PeiPolicyLib/PeiPolicy.c new file mode 100644 index 00000000000..aeeeedf9c96 --- /dev/null +++ b/PolicyServicePkg/Library/PeiPolicyLib/PeiPolicy.c @@ -0,0 +1,38 @@ +/** @file + Implementation of the Verified Policy library for PEI. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include +#include + +#include "../PolicyLibCommon.h" + +/** + A private helper function to retrieve the policy service protocol. + + @param[out] PolicyInterface Returns with the pointer to the protocol. + + @retval EFI_SUCCESS Policy protocol was found. + @retval EFI_NOT_FOUND Policy protocol was not found. +**/ +EFI_STATUS +GetPolicyInterface ( + OUT POLICY_INTERFACE **PolicyInterface + ) +{ + return PeiServicesLocatePpi ( + &gPeiPolicyPpiGuid, + 0, + NULL, + (VOID **)PolicyInterface + ); +} diff --git a/PolicyServicePkg/Library/PeiPolicyLib/PeiPolicyLib.inf b/PolicyServicePkg/Library/PeiPolicyLib/PeiPolicyLib.inf new file mode 100644 index 00000000000..329fbe051d4 --- /dev/null +++ b/PolicyServicePkg/Library/PeiPolicyLib/PeiPolicyLib.inf @@ -0,0 +1,35 @@ +## @file +# PEI instance of verified policy library. +# +# Copyright (c) Microsoft Corporation. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 1.26 + BASE_NAME = PeiPolicyLib + MODULE_UNI_FILE = PeiPolicyLib.uni + FILE_GUID = 62407A0E-707F-4E3D-B661-358FDEA7F252 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + LIBRARY_CLASS = PolicyLib|PEIM PEI_CORE SEC + +[Sources] + ../PolicyLibCommon.c + ../PolicyLibCommon.h + PeiPolicy.c + +[Packages] + MdePkg/MdePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + +[Ppis] + gPeiPolicyPpiGuid ## CONSUMES + +[Pcd] diff --git a/PolicyServicePkg/Library/PeiPolicyLib/PeiPolicyLib.uni b/PolicyServicePkg/Library/PeiPolicyLib/PeiPolicyLib.uni new file mode 100644 index 00000000000..4400ae77a84 --- /dev/null +++ b/PolicyServicePkg/Library/PeiPolicyLib/PeiPolicyLib.uni @@ -0,0 +1,14 @@ +// /** @file +// PEI Instance of Policy Library +// +// Copyright (c) Microsoft Corporation +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "PEI Instance of Policy Library" + +#string STR_MODULE_DESCRIPTION #language en-US "Accesses the policy service and validates policy data" + diff --git a/PolicyServicePkg/Library/PolicyLibCommon.c b/PolicyServicePkg/Library/PolicyLibCommon.c new file mode 100644 index 00000000000..29c978da4c8 --- /dev/null +++ b/PolicyServicePkg/Library/PolicyLibCommon.c @@ -0,0 +1,378 @@ +/** @file + The common implementation of the verified policy library routines. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "../PolicyLibCommon.h" + +/** + Creates or updates a policy in the policy store. Will notify any applicable + callbacks. + + @param[in] PolicyGuid The uniquely identifying GUID for the policy. + @param[in] Attributes Attributes of the policy to be set. + @param[in] Policy The policy data buffer. This buffer will be + copied into the data store. + @param[in] PolicySize The size of the provided policy data. + + @retval EFI_SUCCESS Policy was created or updated. + @retval EFI_ACCESS_DENIED Policy was already finalized prior to this call. + @retval EFI_OUT_OF_RESOURCES Failed to allocate space for policy structures. +**/ +EFI_STATUS +EFIAPI +SetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT64 Attributes, + IN VOID *Policy, + IN UINT16 PolicySize + ) +{ + POLICY_INTERFACE *PolicyService; + EFI_STATUS Status; + + Status = GetPolicyInterface (&PolicyService); + if (!EFI_ERROR (Status)) { + Status = PolicyService->SetPolicy (PolicyGuid, Attributes, Policy, PolicySize); + } + + return Status; +} + +/** + Retrieves the policy descriptor, buffer, and size for a given policy GUID. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + @param[out] Attributes The attributes of the stored policy. + @param[out] Policy The buffer where the policy data is copied. + @param[in,out] PolicySize The size of the stored policy data buffer. + On output, contains the size of the stored policy. + + @retval EFI_SUCCESS The policy was retrieved. + @retval EFI_BUFFER_TOO_SMALL The provided buffer size was too small. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +GetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + OUT UINT64 *Attributes OPTIONAL, + OUT VOID *Policy, + IN OUT UINT16 *PolicySize + ) +{ + POLICY_INTERFACE *PolicyService; + EFI_STATUS Status; + + Status = GetPolicyInterface (&PolicyService); + if (!EFI_ERROR (Status)) { + Status = PolicyService->GetPolicy (PolicyGuid, Attributes, Policy, PolicySize); + } + + return Status; +} + +/** + Removes a policy from the policy store. The policy will be removed from the store + and freed if possible. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + + @retval EFI_SUCCESS The policy was removed. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +RemovePolicy ( + IN CONST EFI_GUID *PolicyGuid + ) +{ + POLICY_INTERFACE *PolicyService; + EFI_STATUS Status; + + Status = GetPolicyInterface (&PolicyService); + if (!EFI_ERROR (Status)) { + Status = PolicyService->RemovePolicy (PolicyGuid); + } + + return Status; +} + +EFI_STATUS +EFIAPI +RegisterPolicyNotify ( + IN CONST EFI_GUID *PolicyGuid, + IN CONST UINT32 EventTypes, + IN CONST UINT32 Priority, + IN POLICY_HANDLER_CALLBACK CallbackRoutine, + OUT VOID **Handle + ) +{ + POLICY_INTERFACE *PolicyService; + EFI_STATUS Status; + + Status = GetPolicyInterface (&PolicyService); + if (!EFI_ERROR (Status)) { + Status = PolicyService->RegisterNotify (PolicyGuid, EventTypes, Priority, CallbackRoutine, Handle); + } + + return Status; +} + +EFI_STATUS +EFIAPI +UnregisterPolicyNotify ( + IN VOID *Handle + ) +{ + POLICY_INTERFACE *PolicyService; + EFI_STATUS Status; + + Status = GetPolicyInterface (&PolicyService); + if (!EFI_ERROR (Status)) { + Status = PolicyService->UnregisterNotify (Handle); + } + + return Status; +} + +/** + Retrieves a verified policy of the given type from the policy store. + + @param[in] PolicyGuid The GUID of policy in the policy store. + + @param[in] Descriptor The descriptor for the verified policy data + structure. + + @param[out] Attributes Returns the attributes of the policy in the + policy store. + + @param[out] DataHandle Returns the handle to the verified policy. + + @retval EFI_SUCCESS The policy was successfully retrieved. + @retval EFI_BAD_BUFFER_SIZE The policy was an unexpected size. + @retval EFI_INCOMPATIBLE_VERSION The verified policy major version did not + match. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +RETURN_STATUS +EFIAPI +GetVerifiedPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN CONST VERIFIED_POLICY_DESCRIPTOR *Descriptor, + OUT UINT64 *Attributes OPTIONAL, + OUT EFI_HANDLE *DataHandle + ) +{ + VOID *Buffer; + UINT16 BufferSize; + POLICY_INTERFACE *PolicyService; + EFI_STATUS Status; + VERIFIED_POLICY_HEADER *Header; + + Buffer = NULL; + BufferSize = 0; + Header = NULL; + *DataHandle = NULL; + + Status = GetPolicyInterface (&PolicyService); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = PolicyService->GetPolicy (PolicyGuid, Attributes, &Buffer, &BufferSize); + if (Status != EFI_BUFFER_TOO_SMALL) { + ASSERT (EFI_ERROR (Status)); + goto Exit; + } + + if (BufferSize < sizeof (VERIFIED_POLICY_HEADER)) { + Status = EFI_BAD_BUFFER_SIZE; + goto Exit; + } + + Buffer = AllocatePool (BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + Status = PolicyService->GetPolicy (PolicyGuid, Attributes, Buffer, &BufferSize); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Header = (VERIFIED_POLICY_HEADER *)Buffer; + if ((Descriptor->Signature != Header->Signature) || + (Descriptor->MajorVersion != Header->MajorVersion)) + { + Status = EFI_INCOMPATIBLE_VERSION; + goto Exit; + } + + if (Descriptor->Size != Header->Size) { + ASSERT (FALSE); + Status = EFI_BAD_BUFFER_SIZE; + goto Exit; + } + + *DataHandle = Buffer; + +Exit: + if (EFI_ERROR (Status) && (Buffer != NULL)) { + FreePool (Buffer); + } + + return Status; +} + +/** + Creates a new verified policy data structure. + + @param[in] Descriptor The descriptor of the verified policy data structure + to be created. + + @param[out] DataHandle The handle to the newly created verified policy data + structure. + + @retval EFI_SUCCESS The data structure was successfully created. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +RETURN_STATUS +EFIAPI +CreateVerifiedPolicy ( + IN CONST VERIFIED_POLICY_DESCRIPTOR *Descriptor, + OUT EFI_HANDLE *DataHandle + ) +{ + VOID *Buffer; + VERIFIED_POLICY_HEADER *Header; + UINT32 PolicySize; + + *DataHandle = NULL; + + PolicySize = sizeof (VERIFIED_POLICY_HEADER) + Descriptor->Size; + Buffer = AllocatePool (PolicySize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (Buffer, PolicySize); + Header = (VERIFIED_POLICY_HEADER *)Buffer; + Header->Signature = Descriptor->Signature; + Header->MajorVersion = Descriptor->MajorVersion; + Header->MinorVersion = Descriptor->MinorVersion; + Header->Size = Descriptor->Size; + *DataHandle = Buffer; + return EFI_SUCCESS; +} + +/** + Write a verified policy to the policy store. + + @param[in] PolicyGuid The GUID of policy in the policy store. + + @param[in] Attributes The attributes to set in the policy store. + + @param[in] DataHandle The handle to the policy data. + + @retval EFI_SUCCESS The policy was successfully retrieved. + @retval EFI_INVALID_PARAMETER DataHandle is NULL. + @retval EFI_BAD_BUFFER_SIZE The policy is too large. +**/ +RETURN_STATUS +EFIAPI +SetVerifiedPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT64 Attributes, + IN EFI_HANDLE DataHandle + ) +{ + UINT16 BufferSize; + POLICY_INTERFACE *PolicyService; + EFI_STATUS Status; + VERIFIED_POLICY_HEADER *Header; + UINT32 PolicySize; + + if (DataHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + Header = (VERIFIED_POLICY_HEADER *)DataHandle; + PolicySize = sizeof (VERIFIED_POLICY_HEADER) + Header->Size; + if (PolicySize > MAX_UINT16) { + return EFI_BAD_BUFFER_SIZE; + } + + Status = GetPolicyInterface (&PolicyService); + if (EFI_ERROR (Status)) { + return Status; + } + + BufferSize = (UINT16)PolicySize; + return PolicyService->SetPolicy (PolicyGuid, Attributes, DataHandle, BufferSize); +} + +/** + Closes a policy data handle. + + @param[in] DataHandle The policy handle to be closed. + + @retval EFI_SUCCESS The policy handle was successfully + closed. + @retval EFI_INVALID_PARAMETER The data handle is NULL. +**/ +RETURN_STATUS +EFIAPI +CloseVerifiedPolicy ( + IN EFI_HANDLE DataHandle + ) +{ + if (DataHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + FreePool (DataHandle); + return EFI_SUCCESS; +} + +/** + Records access to a policy data structure used by autogenerated code. This + function should not be manually called. + + @param[in] DataHandle The policy handle where the access was made. + @param[in] CallerGuid The file guid of the caller for tracking access. + @param[in] Offset The offset into the policy data for the access. + @param[in] Size The size of the access. + @param[in] Write Indicates if the policy was written to. + +**/ +VOID +EFIAPI +ReportVerifiedPolicyAccess ( + IN EFI_HANDLE DataHandle, + IN CONST EFI_GUID *CallerGuid, + IN UINT32 Offset, + IN UINT32 Size, + IN BOOLEAN Write + ) + +{ + // + // Currently a no-op, but leaving room for future checks and analytics. + // + + return; +} diff --git a/PolicyServicePkg/Library/PolicyLibCommon.h b/PolicyServicePkg/Library/PolicyLibCommon.h new file mode 100644 index 00000000000..b5973192c83 --- /dev/null +++ b/PolicyServicePkg/Library/PolicyLibCommon.h @@ -0,0 +1,17 @@ +/** @file + Common internal definitions for the policy library + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _POLICY_LIB_COMMON_H_ +#define _POLICY_LIB_COMMON_H_ + +EFI_STATUS +GetPolicyInterface ( + OUT POLICY_INTERFACE **PolicyInterface + ); + +#endif diff --git a/PolicyServicePkg/Library/README.md b/PolicyServicePkg/Library/README.md new file mode 100644 index 00000000000..821292a1611 --- /dev/null +++ b/PolicyServicePkg/Library/README.md @@ -0,0 +1,63 @@ +# Policy Library + +The policy library is responsible for handling more robust features on top of +the basic policy store provided by the policy. The primary use case of this +library would be to use _verified policies_ stored in the policy store. + +## Verified Policy Overview + +Verified policy is a set of library functions and tools around policy that +provides a more type and version safe way of sharing data structures between +components in different code bases or firmware layers. Using the verified +policy functions in the library, combined with a per-data-structure generated +header, callers can set and read data from a policy with automatic checks and +guarantees to ensure the data being read correctly. This also provides useful +concepts such as default values allowing for structure-safe version mismatch. + +## Verified Policy Components + +The following are a few key components for utilizing verified policies. + +__Policy Guid__ - As with all policies, a single instance of a policy should +have a unique GUID. This GUID is not associated with the policy structure and +only represents the data instance, not the data type or structure. + +__Verified Policy Descriptor__ - The verified policy descriptor is a auto +generated structure which describes the expected data structure. This includes +a unique tag for the data, version info, size info, etc. The policy library will +use this information to check the policy data to ensure it matches the expected +type and required version. + +__Attributes__ - Similar to all other policies, attributes can be used to +enforce access restrictions on the policy instance. + +__Verified Policy Handle__ - To enforce the verified policy abstraction, the +caller will not be provided a direct pointer to the data. Instead they will be +provided a policy handle which should be used with the auto-generated accessors. +This allows for automatic checks to be done to ensure safe access of data. + +## Verified Policy Functions + +The policy library provides the following functions for accessing verified +policy. + +___GetVerifiedPolicy___ - Retrieves a verified policy from the policy store, +checking that the data is the expected type and version. The returned handle +references a copy of the policy and so a set is required to store and changes. + +___CreateVerifiedPolicy___ - Creates a new verified policy data instance. This +routine does not add the data to the policy store. + +___SetVerifiedPolicy___ - Sets the provided verified policy data into the policy +store. + +___CloseVerifiedPolicy___ - Closes the local handle to the verified policy, +freeing and resources. + +___ReportVerifiedPolicyAccess___ - A API used to communicate access between the +generated accessors and the generic library. This routine should not be manually +called. + +## Generating Verified Policy Headers + +Details regarding generating verified policies are not yet documented here. diff --git a/PolicyServicePkg/PolicyService/DxeMm/PolicyCommon.c b/PolicyServicePkg/PolicyService/DxeMm/PolicyCommon.c new file mode 100644 index 00000000000..8850a12ff58 --- /dev/null +++ b/PolicyServicePkg/PolicyService/DxeMm/PolicyCommon.c @@ -0,0 +1,550 @@ +/** @file + Common function implementations for storing and finding policies for the DXE/MM + policy service modules. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PolicyCommon.h" +#include "PolicyInterface.h" + +LIST_ENTRY mPolicyListHead = INITIALIZE_LIST_HEAD_VARIABLE (mPolicyListHead); +LIST_ENTRY mNotifyListHead = INITIALIZE_LIST_HEAD_VARIABLE (mNotifyListHead); + +// +// Global state for the potentially recursive notify routine. +// + +BOOLEAN mNotifyInProgress = FALSE; +BOOLEAN mCallbacksDeleted = FALSE; + +/** + Retrieves the policy descriptor, buffer, and size for a given policy GUID. + Assumes the caller is already in CS. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + @param[out] PolicyDescriptor Descriptor for the stored policy. + @param[out] Policy The buffer where the policy data is copied. + @param[in,out] PolicySize The size of the stored policy data buffer. + On output, contains the size of the stored policy. + + @retval EFI_SUCCESS The policy was retrieved. + @retval EFI_BUFFER_TOO_SMALL The provided buffer size was too small. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +GetPolicyEntry ( + IN CONST EFI_GUID *PolicyGuid, + OUT POLICY_ENTRY **PolicyEntry + ) +{ + LIST_ENTRY *Link; + POLICY_ENTRY *CheckEntry; + + BASE_LIST_FOR_EACH (Link, &mPolicyListHead) { + CheckEntry = POLICY_ENTRY_FROM_LINK (Link); + if (CompareGuid (PolicyGuid, &CheckEntry->PolicyGuid)) { + *PolicyEntry = CheckEntry; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Checks if a given policy exists in the policy store. Assumes the caller + is already in CS. + + @param[in] PolicyGuid The policy GUID to match. + + @retval TRUE The policy exists in the store. + @retval FALSE The policy does not exists in the store. +**/ +BOOLEAN +EFIAPI +CheckPolicyExists ( + IN CONST EFI_GUID *PolicyGuid + ) +{ + POLICY_ENTRY *Entry; + + return !EFI_ERROR (GetPolicyEntry (PolicyGuid, &Entry)); +} + +/** + Retrieves the policy descriptor, buffer, and size for a given policy GUID. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + @param[out] Attributes The attributes of the stored policy. + @param[out] Policy The buffer where the policy data is copied. + @param[in,out] PolicySize The size of the stored policy data buffer. + On output, contains the size of the stored policy. + + @retval EFI_SUCCESS The policy was retrieved. + @retval EFI_BUFFER_TOO_SMALL The provided buffer size was too small. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +CommonGetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + OUT UINT64 *Attributes OPTIONAL, + OUT VOID *Policy, + IN OUT UINT16 *PolicySize + ) +{ + EFI_STATUS Status; + POLICY_ENTRY *Entry; + + if ((PolicyGuid == NULL) || + (PolicySize == NULL) || + ((Policy == NULL) && (*PolicySize != 0))) + { + return EFI_INVALID_PARAMETER; + } + + PolicyLockAcquire (); + Status = GetPolicyEntry (PolicyGuid, &Entry); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (Attributes != NULL) { + *Attributes = Entry->Attributes; + } + + if (*PolicySize < Entry->PolicySize) { + *PolicySize = Entry->PolicySize; + Status = EFI_BUFFER_TOO_SMALL; + goto Exit; + } + + CopyMem (Policy, Entry->Policy, Entry->PolicySize); + *PolicySize = Entry->PolicySize; + +Exit: + PolicyLockRelease (); + return Status; +} + +/** + Parses the HOB list to find active policies to add to the policy store. + + @retval EFI_SUCCESS Policies added to the policy store. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for policy. +**/ +EFI_STATUS +EFIAPI +IngestPoliciesFromHob ( + VOID + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + POLICY_HOB_HEADER *PolicyHob; + POLICY_ENTRY *PolicyEntry; + UINT32 PolicyCount; + EFI_STATUS Status; + + PolicyCount = 0; + Status = EFI_SUCCESS; + PolicyLockAcquire (); + for (GuidHob = GetFirstGuidHob (&gPolicyHobGuid); + GuidHob != NULL; + GuidHob = GetNextGuidHob (&gPolicyHobGuid, GET_NEXT_HOB (GuidHob))) + { + PolicyHob = (POLICY_HOB_HEADER *)GET_GUID_HOB_DATA (GuidHob); + if (PolicyHob->Removed) { + continue; + } + + if (PolicyHob->Attributes & POLICY_ATTRIBUTE_PEI_ONLY) { + continue; + } + + ASSERT (!CheckPolicyExists (&PolicyHob->PolicyGuid)); + + PolicyEntry = AllocatePool (sizeof (POLICY_ENTRY)); + if (PolicyEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + ZeroMem (PolicyEntry, sizeof (POLICY_ENTRY)); + PolicyEntry->Signature = POLICY_ENTRY_SIGNATURE; + PolicyEntry->PolicyGuid = PolicyHob->PolicyGuid; + PolicyEntry->FromHob = TRUE; + PolicyEntry->Attributes = PolicyHob->Attributes; + PolicyEntry->Policy = GET_HOB_POLICY_DATA (PolicyHob); + PolicyEntry->PolicySize = PolicyHob->PolicySize; + PolicyEntry->AllocationSize = 0; + InsertTailList (&mPolicyListHead, &PolicyEntry->Link); + PolicyCount++; + + if (PolicyEntry->Attributes & POLICY_ATTRIBUTE_FINALIZED) { + Status = InstallPolicyIndicatorProtocol (&PolicyEntry->PolicyGuid); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to install notification protocol. (%r)\n", __FUNCTION__, Status)); + } + } + } + + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "Found %d active policies in HOBs.\n", PolicyCount)); + } + + PolicyLockRelease (); + return Status; +} + +/** + Removes a policy from the policy store. The policy will be removed from the store + and freed if possible. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + + @retval EFI_SUCCESS The policy was removed. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +CommonRemovePolicy ( + IN CONST EFI_GUID *PolicyGuid + ) +{ + EFI_STATUS Status; + POLICY_ENTRY *Entry; + + if (PolicyGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + PolicyLockAcquire (); + Status = GetPolicyEntry (PolicyGuid, &Entry); + if (!EFI_ERROR (Status)) { + RemoveEntryList (&Entry->Link); + if (!Entry->FromHob) { + FreePool (Entry->Policy); + } + + Entry->FreeAfterNotify = TRUE; + CommonPolicyNotify (POLICY_NOTIFY_REMOVED, Entry); + } + + PolicyLockRelease (); + return Status; +} + +/** + Creates or updates a policy in the policy store. Will notify any applicable + callbacks. + + @param[in] PolicyGuid The uniquely identifying GUID for the policy. + @param[in] Attributes Attributes of the policy to be set. + @param[in] Policy The policy data buffer. This buffer will be + copied into the data store. + @param[in] PolicySize The size of the provided policy data. + + @retval EFI_SUCCESS Policy was created or updated. + @retval EFI_ACCESS_DENIED Policy was already finalized prior to this call. + @retval EFI_OUT_OF_RESOURCES Failed to allocate space for policy structures. +**/ +EFI_STATUS +EFIAPI +CommonSetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT64 Attributes, + IN VOID *Policy, + IN UINT16 PolicySize + ) +{ + EFI_STATUS Status; + POLICY_ENTRY *Entry; + VOID *AllocatedPolicy; + UINT32 Events; + + if ((PolicyGuid == NULL) || (Policy == NULL) || (PolicySize == 0)) { + return EFI_INVALID_PARAMETER; + } + + PolicyLockAcquire (); + Status = GetPolicyEntry (PolicyGuid, &Entry); + if (!EFI_ERROR (Status)) { + if (Entry->Attributes & POLICY_ATTRIBUTE_FINALIZED) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // Re-use the buffer if possible + if (!Entry->FromHob && (PolicySize <= Entry->AllocationSize)) { + CopyMem (Entry->Policy, Policy, PolicySize); + Entry->PolicySize = PolicySize; + Entry->Attributes = Attributes; + } else { + AllocatedPolicy = AllocatePool (PolicySize); + if (AllocatedPolicy == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem (AllocatedPolicy, Policy, PolicySize); + if (!Entry->FromHob) { + FreePool (Entry->Policy); + } + + Entry->Policy = AllocatedPolicy; + Entry->Attributes = Attributes; + Entry->PolicySize = PolicySize; + Entry->AllocationSize = PolicySize; + Entry->FromHob = FALSE; + } + } else { + Entry = AllocatePool (sizeof (POLICY_ENTRY)); + if (Entry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + AllocatedPolicy = AllocatePool (PolicySize); + if (AllocatedPolicy == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + ZeroMem (Entry, sizeof (POLICY_ENTRY)); + Entry->Signature = POLICY_ENTRY_SIGNATURE; + Entry->PolicyGuid = *PolicyGuid; + Entry->Attributes = Attributes; + Entry->Policy = AllocatedPolicy; + Entry->PolicySize = PolicySize; + Entry->AllocationSize = PolicySize; + CopyMem (Entry->Policy, Policy, PolicySize); + InsertTailList (&mPolicyListHead, &Entry->Link); + Status = EFI_SUCCESS; + } + + Events = POLICY_NOTIFY_SET | (Entry->Attributes & POLICY_ATTRIBUTE_FINALIZED ? POLICY_NOTIFY_FINALIZED : 0); + CommonPolicyNotify (Events, Entry); + + if (Attributes & POLICY_ATTRIBUTE_FINALIZED) { + Status = InstallPolicyIndicatorProtocol (PolicyGuid); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to install notification protocol. (%r)\n", __FUNCTION__, Status)); + goto Exit; + } + } + +Exit: + PolicyLockRelease (); + return Status; +} + +/** + Registers a callback for a policy event notification. The provided routine + will be invoked when one of multiple of the provided event types for the specified + guid occurs. + + @param[in] PolicyGuid The GUID of the policy the being watched. + @param[in] EventTypes The events to notify the callback for. + @param[in] Priority The priority of the callback where the lower values + will be called first. + @param[in] CallbackRoutine The function pointer of the callback to be invoked. + @param[out] Handle Returns the handle to this callback entry. + + @retval EFI_SUCCESS The callback notification as successfully registered. + @retval EFI_INVALID_PARAMETER EventTypes was 0 or Callback routine is invalid. + @retval EFI_OUT_OF_RESOURCES Tracking data for callback could not be allocated. + @retval Other The callback registration failed. +**/ +EFI_STATUS +EFIAPI +CommonRegisterNotify ( + IN CONST EFI_GUID *PolicyGuid, + IN CONST UINT32 EventTypes, + IN CONST UINT32 Priority, + IN POLICY_HANDLER_CALLBACK CallbackRoutine, + OUT VOID **Handle + ) +{ + LIST_ENTRY *Link; + POLICY_NOTIFY_ENTRY *CheckEntry; + POLICY_NOTIFY_ENTRY *Entry; + + if ((CallbackRoutine == NULL) || + (PolicyGuid == NULL) || + (Handle == NULL) || + ((EventTypes & POLICY_NOTIFY_ALL) != EventTypes)) + { + return EFI_INVALID_PARAMETER; + } + + Entry = AllocatePool (sizeof (POLICY_NOTIFY_ENTRY)); + if (Entry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (Entry, sizeof (POLICY_NOTIFY_ENTRY)); + Entry->Signature = POLICY_NOTIFY_ENTRY_SIGNATURE; + Entry->PolicyGuid = *PolicyGuid; + Entry->EventTypes = EventTypes; + Entry->Priority = Priority; + Entry->CallbackRoutine = CallbackRoutine; + + // + // Find the entry to insert this new entry before. If the list is empty then + // this is the head. + // + + BASE_LIST_FOR_EACH (Link, &mNotifyListHead) { + CheckEntry = POLICY_NOTIFY_ENTRY_FROM_LINK (Link); + if (CheckEntry->Priority > Priority) { + break; + } + } + + InsertTailList (Link, &Entry->Link); + *Handle = (VOID *)Entry; + return EFI_SUCCESS; +} + +/** + Removes a registered notification callback. + + @param[in] Handle The handle for the registered callback. + + @retval EFI_SUCCESS The callback notification as successfully removed. + @retval EFI_INVALID_PARAMETER The provided handle is invalid. + @retval EFI_NOT_FOUND The provided handle could not be found. +**/ +EFI_STATUS +EFIAPI +CommonUnregisterNotify ( + IN VOID *Handle + ) +{ + POLICY_NOTIFY_ENTRY *Entry; + + if (Handle == NULL) { + return EFI_INVALID_PARAMETER; + } + + Entry = (POLICY_NOTIFY_ENTRY *)Handle; + if (Entry->Signature != POLICY_NOTIFY_ENTRY_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + if (!IsNodeInList (&mNotifyListHead, &Entry->Link)) { + ASSERT (FALSE); + return EFI_NOT_FOUND; + } + + // + // If this entry is the entry that is currently being processed, step the entry + // forward. + // + if (mNotifyInProgress) { + Entry->Tombstone = TRUE; + mCallbacksDeleted = TRUE; + } else { + RemoveEntryList (&Entry->Link); + FreePool (Entry); + } + + return EFI_SUCCESS; +} + +/** + Notifies all registered callbacks of a policy event. + + @param[in] EventTypes The event that occurred. + @param[in] PolicyEntry The policy entry the event occurred for. +**/ +VOID +EFIAPI +CommonPolicyNotify ( + IN CONST UINT32 EventTypes, + IN POLICY_ENTRY *PolicyEntry + ) +{ + LIST_ENTRY *Link; + POLICY_NOTIFY_ENTRY *NotifyEntry; + BOOLEAN FirstNotification; + UINT32 Depth; + + // + // Global recursion handling. + // + + FirstNotification = !mNotifyInProgress; + mNotifyInProgress = TRUE; + + // + // Per-policy recursion handling. + // + + if (PolicyEntry->NotifyDepth == MAX_UINT32) { + DEBUG ((DEBUG_ERROR, "%a: Maximum recursion hit in policy notification\n", __FUNCTION__)); + ASSERT (FALSE); + return; + } + + PolicyEntry->NotifyDepth++; + Depth = PolicyEntry->NotifyDepth; + + BASE_LIST_FOR_EACH (Link, &mNotifyListHead) { + NotifyEntry = POLICY_NOTIFY_ENTRY_FROM_LINK (Link); + if (CompareGuid (&PolicyEntry->PolicyGuid, &NotifyEntry->PolicyGuid) && + ((EventTypes & NotifyEntry->EventTypes) != 0) && + !NotifyEntry->Tombstone) + { + NotifyEntry->CallbackRoutine (&PolicyEntry->PolicyGuid, EventTypes, NotifyEntry); + + // + // If there was a newer notify then this notification is now moot. + // + + if (PolicyEntry->NotifyDepth != Depth) { + break; + } + } + } + + // + // If this is the top of the stack for the policy then clear that depth and + // free in the case that the policy was removed during the callbacks. + // + + if (Depth == 1) { + PolicyEntry->NotifyDepth = 0; + if (PolicyEntry->FreeAfterNotify) { + FreePool (PolicyEntry); + PolicyEntry = NULL; + } + } + + // + // To simplify potential link list edge cases with the recursion, removed + // callbacks that occur during the recursion are only tomb-stoned, if this + // is the top of the stack then go through and remove those. + // + + if (FirstNotification) { + if (mCallbacksDeleted) { + BASE_LIST_FOR_EACH (Link, &mNotifyListHead) { + NotifyEntry = POLICY_NOTIFY_ENTRY_FROM_LINK (Link); + if (NotifyEntry->Tombstone) { + Link = Link->BackLink; + RemoveEntryList (&NotifyEntry->Link); + FreePool (NotifyEntry); + } + } + + mCallbacksDeleted = FALSE; + } + + mNotifyInProgress = FALSE; + } +} diff --git a/PolicyServicePkg/PolicyService/DxeMm/PolicyCommon.h b/PolicyServicePkg/PolicyService/DxeMm/PolicyCommon.h new file mode 100644 index 00000000000..a3c8c3302a3 --- /dev/null +++ b/PolicyServicePkg/PolicyService/DxeMm/PolicyCommon.h @@ -0,0 +1,221 @@ +/** @file + Common prototypes and type definitions for the DXE/MM Policy service + modules. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _POLICY_COMMON_H_ +#define _POLICY_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../PolicyHob.h" + +typedef struct _POLICY_ENTRY { + UINT32 Signature; + LIST_ENTRY Link; + EFI_GUID PolicyGuid; + UINT64 Attributes; + BOOLEAN FromHob; + VOID *Policy; + UINT16 PolicySize; + UINTN AllocationSize; + UINT32 NotifyDepth; + BOOLEAN FreeAfterNotify; +} POLICY_ENTRY; + +#define POLICY_ENTRY_SIGNATURE SIGNATURE_32('p', 'o', 'l', 'c') +#define POLICY_ENTRY_FROM_LINK(a) CR (a, POLICY_ENTRY, Link, POLICY_ENTRY_SIGNATURE) + +typedef struct _POLICY_NOTIFY_ENTRY { + UINT32 Signature; + LIST_ENTRY Link; + EFI_GUID PolicyGuid; + UINT32 EventTypes; + UINT32 Priority; + POLICY_HANDLER_CALLBACK CallbackRoutine; + BOOLEAN Tombstone; +} POLICY_NOTIFY_ENTRY; + +#define POLICY_NOTIFY_ENTRY_SIGNATURE SIGNATURE_32('p', 'o', 'l', 'n') +#define POLICY_NOTIFY_ENTRY_FROM_LINK(a) CR (a, POLICY_NOTIFY_ENTRY, Link, POLICY_NOTIFY_ENTRY_SIGNATURE) + +// +// Macros for managing the critical section. +// + +#define POLICY_CS_INIT EFI_TPL OldTpl +#define POLICY_CS_ENTER OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL) +#define POLICY_CS_EXIT gBS->RestoreTPL (OldTpl) + +/** + Creates or updates a policy in the policy store. Will notify any applicable + callbacks. + + @param[in] PolicyGuid The uniquely identifying GUID for the policy. + @param[in] Attributes Attributes of the policy to be set. + @param[in] Policy The policy data buffer. This buffer will be + copied into the data store. + @param[in] PolicySize The size of the provided policy data. + + @retval EFI_SUCCESS Policy was created or updated. + @retval EFI_ACCESS_DENIED Policy was already finalized prior to this call. + @retval EFI_OUT_OF_RESOURCES Failed to allocate space for policy structures. +**/ +EFI_STATUS +EFIAPI +CommonSetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT64 Attributes, + IN VOID *Policy, + IN UINT16 PolicySize + ); + +/** + Retrieves the policy descriptor, buffer, and size for a given policy GUID. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + @param[out] Attributes The attributes of the stored policy. + @param[out] Policy The buffer where the policy data is copied. + @param[in,out] PolicySize The size of the stored policy data buffer. + On output, contains the size of the stored policy. + + @retval EFI_SUCCESS The policy was retrieved. + @retval EFI_BUFFER_TOO_SMALL The provided buffer size was too small. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +CommonGetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + OUT UINT64 *Attributes OPTIONAL, + OUT VOID *Policy, + IN OUT UINT16 *PolicySize + ); + +/** + Removes a policy from the policy store. The policy will be removed from the store + and freed if possible. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + + @retval EFI_SUCCESS The policy was removed. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +CommonRemovePolicy ( + IN CONST EFI_GUID *PolicyGuid + ); + +/** + Acquires the environment specific lock for the policy list. + +**/ +VOID +EFIAPI +PolicyLockAcquire ( + VOID + ); + +/** + Release the environment specific lock for the policy list. + +**/ +VOID +EFIAPI +PolicyLockRelease ( + VOID + ); + +/** + Creates and empty protocol for a given GUID to notify or dispatch consumers of + this policy GUID. If the protocol already exists it will be reinstalled. + + @param[in] PolicyGuid The policy GUID used for the protocol. + + @retval EFI_SUCCESS The protocol was installed or reinstalled. +**/ +EFI_STATUS +EFIAPI +InstallPolicyIndicatorProtocol ( + IN CONST EFI_GUID *PolicyGuid + ); + +/** + Parses the HOB list to find active policies to add to the policy store. + + @retval EFI_SUCCESS Policies added to the policy store. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for policy. +**/ +EFI_STATUS +EFIAPI +IngestPoliciesFromHob ( + VOID + ); + +/** + Registers a callback for a policy event notification. The provided routine + will be invoked when one of multiple of the provided event types for the specified + guid occurs. + + @param[in] PolicyGuid The GUID of the policy the being watched. + @param[in] EventTypes The events to notify the callback for. + @param[in] Priority The priority of the callback where the lower values + will be called first. + @param[in] CallbackRoutine The function pointer of the callback to be invoked. + @param[out] Handle Returns the handle to this callback entry. + + @retval EFI_SUCCESS The callback notification as successfully registered. + @retval EFI_INVALID_PARAMETER EventTypes was 0 or Callback routine is invalid. + @retval Other The callback registration failed. +**/ +EFI_STATUS +EFIAPI +CommonRegisterNotify ( + IN CONST EFI_GUID *PolicyGuid, + IN CONST UINT32 EventTypes, + IN CONST UINT32 Priority, + IN POLICY_HANDLER_CALLBACK CallbackRoutine, + OUT VOID **Handle + ); + +/** + Removes a registered notification callback. + + @param[in] Handle The handle for the registered callback. + + @retval EFI_SUCCESS The callback notification as successfully removed. + @retval EFI_INVALID_PARAMETER The provided handle is invalid. + @retval EFI_NOT_FOUND The provided handle could not be found. +**/ +EFI_STATUS +EFIAPI +CommonUnregisterNotify ( + IN VOID *Handle + ); + +/** + Notifies all registered callbacks of a policy event. + + @param[in] EventTypes The event that occurred. + @param[in] PolicyEntry The policy entry the event occurred for. +**/ +VOID +EFIAPI +CommonPolicyNotify ( + IN CONST UINT32 EventTypes, + IN POLICY_ENTRY *PolicyEntry + ); + +#endif diff --git a/PolicyServicePkg/PolicyService/DxeMm/PolicyDxe.c b/PolicyServicePkg/PolicyService/DxeMm/PolicyDxe.c new file mode 100644 index 00000000000..bdf13a6c156 --- /dev/null +++ b/PolicyServicePkg/PolicyService/DxeMm/PolicyDxe.c @@ -0,0 +1,122 @@ +/** @file + Implements the DXE policy protocol, providing services to publish and access + system policy. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PolicyCommon.h" +#include +#include +#include + +STATIC EFI_LOCK mPolicyListLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY); +STATIC EFI_HANDLE mImageHandle = NULL; + +POLICY_PROTOCOL mPolicyProtocol = { + CommonSetPolicy, + CommonGetPolicy, + CommonRemovePolicy, + CommonRegisterNotify, + CommonUnregisterNotify +}; + +/** + Acquires the environment specific lock for the policy list. + +**/ +VOID +EFIAPI +PolicyLockAcquire ( + VOID + ) +{ + EfiAcquireLock (&mPolicyListLock); +} + +/** +Release the environment specific lock for the policy list. + +**/ +VOID +EFIAPI +PolicyLockRelease ( + VOID + ) +{ + EfiReleaseLock (&mPolicyListLock); +} + +/** + Creates an empty protocol for a given GUID to notify or dispatch consumers of + this policy GUID. If the protocol already exists it will be reinstalled. + + @param[in] PolicyGuid The policy GUID used for the protocol. + + @retval EFI_SUCCESS The protocol was installed or reinstalled. +**/ +EFI_STATUS +EFIAPI +InstallPolicyIndicatorProtocol ( + IN CONST EFI_GUID *PolicyGuid + ) +{ + EFI_STATUS Status; + VOID *Interface; + + Status = gBS->LocateProtocol ((EFI_GUID *)PolicyGuid, NULL, &Interface); + if (EFI_ERROR (Status)) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &mImageHandle, + PolicyGuid, + NULL, + NULL + ); + } else { + Status = gBS->ReinstallProtocolInterface ( + mImageHandle, + (EFI_GUID *)PolicyGuid, + NULL, + NULL + ); + } + + return Status; +} + +/** + DXE policy driver entry point. Initialized the policy store from the HOB list + and install the DXE policy protocol. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable UNUSED. + + @retval EFI_SUCCESS Policy store initialized and protocol installed. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for policy and global structures. +**/ +EFI_STATUS +EFIAPI +DxePolicyEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // Process the HOBs to consume any existing policies. + mImageHandle = ImageHandle; + Status = IngestPoliciesFromHob (); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to ingest HOB policies. (%r)\n", __FUNCTION__, Status)); + return Status; + } + + return gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gPolicyProtocolGuid, + &mPolicyProtocol, + NULL + ); +} diff --git a/PolicyServicePkg/PolicyService/DxeMm/PolicyDxe.inf b/PolicyServicePkg/PolicyService/DxeMm/PolicyDxe.inf new file mode 100644 index 00000000000..b264d7d0e63 --- /dev/null +++ b/PolicyServicePkg/PolicyService/DxeMm/PolicyDxe.inf @@ -0,0 +1,46 @@ +## @file +# +# This is a driver for DXE policy service module. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = PolicyDxe + FILE_GUID = fa1bb8af-71f0-4e2e-ba78-0fb10f8367dd + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DxePolicyEntry + +[Sources] + PolicyDxe.c + PolicyCommon.c + PolicyCommon.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseLib + UefiDriverEntryPoint + DebugLib + HobLib + UefiRuntimeServicesTableLib + MemoryAllocationLib + UefiLib + +[Pcd] + +[Guids] + gPolicyHobGuid + +[Protocols] + gPolicyProtocolGuid ## PRODUCES + +[Depex] + TRUE diff --git a/PolicyServicePkg/PolicyService/DxeMm/PolicyMm.c b/PolicyServicePkg/PolicyService/DxeMm/PolicyMm.c new file mode 100644 index 00000000000..bb2274fc712 --- /dev/null +++ b/PolicyServicePkg/PolicyService/DxeMm/PolicyMm.c @@ -0,0 +1,122 @@ +/** @file + Implements the Standalone MM policy protocol, providing services to publish and + access system policy. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include "PolicyCommon.h" +#include + +STATIC EFI_HANDLE mProtocolHandle = NULL; + +MM_POLICY_PROTOCOL mPolicyProtocol = { + CommonSetPolicy, + CommonGetPolicy, + CommonRemovePolicy, + CommonRegisterNotify, + CommonUnregisterNotify +}; + +/** + Acquires the environment specific lock for the policy list. + +**/ +VOID +EFIAPI +PolicyLockAcquire ( + VOID + ) +{ + // Nothing to do. +} + +/** +Release the environment specific lock for the policy list. + +**/ +VOID +EFIAPI +PolicyLockRelease ( + VOID + ) +{ + // Nothing to do. +} + +/** + Creates and empty protocol for a given GUID to notify or dispatch consumers of + this policy GUID. If the protocol already exists it will be reinstalled. + + @param[in] PolicyGuid The policy GUID used for the protocol. + + @retval EFI_SUCCESS The protocol was installed or reinstalled. +**/ +EFI_STATUS +EFIAPI +InstallPolicyIndicatorProtocol ( + IN CONST EFI_GUID *PolicyGuid + ) +{ + EFI_STATUS Status; + + // + // Attempt to uninstall to make sure to republish on update. It is okay if this + // fails. + // + + if (mProtocolHandle != NULL) { + gMmst->MmUninstallProtocolInterface ( + mProtocolHandle, + (EFI_GUID *)PolicyGuid, + NULL + ); + } + + Status = gMmst->MmInstallProtocolInterface ( + &mProtocolHandle, + (EFI_GUID *)PolicyGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + + return Status; +} + +/** + Entry to the Standalone MM policy service module. + + @param[in] ImageHandle The image handle. + @param[in] SystemTable The system table. + + @retval Status From internal routine or boot object, should not fail +**/ +EFI_STATUS +EFIAPI +PolicyStandaloneEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *SystemTable + ) + +{ + EFI_STATUS Status; + + // Process the HOBs to consume any existing policies. + Status = IngestPoliciesFromHob (); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to ingest HOB policies. (%r)\n", __FUNCTION__, Status)); + return Status; + } + + Status = gMmst->MmInstallProtocolInterface ( + &mProtocolHandle, + &gMmPolicyProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPolicyProtocol + ); + + return Status; +} diff --git a/PolicyServicePkg/PolicyService/DxeMm/PolicyMm.inf b/PolicyServicePkg/PolicyService/DxeMm/PolicyMm.inf new file mode 100644 index 00000000000..fc3bfecb5b3 --- /dev/null +++ b/PolicyServicePkg/PolicyService/DxeMm/PolicyMm.inf @@ -0,0 +1,46 @@ +## @file +# +# This is a driver for MM policy service module. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010017 + PI_SPECIFICATION_VERSION = 0x00010032 + BASE_NAME = PolicyMm + FILE_GUID = 9FF65AAD-5982-4609-9702-05EFD584148C + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + ENTRY_POINT = PolicyStandaloneEntry + +[Sources] + PolicyMm.c + PolicyCommon.c + PolicyCommon.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + HobLib + StandaloneMmDriverEntryPoint + MmServicesTableLib + MemoryAllocationLib + +[Pcd] + +[Guids] + gPolicyHobGuid + +[Protocols] + gMmPolicyProtocolGuid ## PRODUCES + +[Depex] + TRUE diff --git a/PolicyServicePkg/PolicyService/DxeMm/UnitTest/DxeMmPolicyUnitTest.c b/PolicyServicePkg/PolicyService/DxeMm/UnitTest/DxeMmPolicyUnitTest.c new file mode 100644 index 00000000000..3d6bcf7c276 --- /dev/null +++ b/PolicyServicePkg/PolicyService/DxeMm/UnitTest/DxeMmPolicyUnitTest.c @@ -0,0 +1,474 @@ +/** @file + Host based unit tests for the Dxe/MM policy service module. + + Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UNIT_TEST_NAME "DXE/MM Policy Service Unit Test" +#define UNIT_TEST_VERSION "1.0" + +// +// Reaching into the binary for testing. +// + +extern LIST_ENTRY mPolicyListHead; +extern LIST_ENTRY mNotifyListHead; +extern BOOLEAN mNotifyInProgress; +extern BOOLEAN mCallbacksDeleted; + +// +// Internal tracking. +// + +typedef struct _NOTIFICATION_TRACKER { + EFI_GUID Guid; + UINT32 Events; + UINT32 Priority; +} NOTIFICATION_TRACKER; + +NOTIFICATION_TRACKER Notifications[100] = { 0 }; +UINT32 NotificationsCount = 0; +UINT32 NotifyCountUpdateRemove = 0; + +// +// Boiler plate functions required by PolicyCommon +// + +VOID +EFIAPI +PolicyLockAcquire ( + VOID + ) +{ + return; +} + +VOID +EFIAPI +PolicyLockRelease ( + VOID + ) +{ + return; +} + +EFI_STATUS +EFIAPI +InstallPolicyIndicatorProtocol ( + IN CONST EFI_GUID *PolicyGuid + ) +{ + return EFI_SUCCESS; +} + +/** + Cleans up the policy and test state. + + @param[in] Context Unused. +**/ +VOID +EFIAPI +PolicyServiceCleanup ( + IN UNIT_TEST_CONTEXT Context + ) +{ + POLICY_NOTIFY_ENTRY *NotifyEntry; + POLICY_ENTRY *PolicyEntry; + + mNotifyInProgress = FALSE; + mCallbacksDeleted = FALSE; + + while (!IsListEmpty (&mNotifyListHead)) { + NotifyEntry = POLICY_NOTIFY_ENTRY_FROM_LINK (mNotifyListHead.ForwardLink); + RemoveEntryList (&NotifyEntry->Link); + FreePool (NotifyEntry); + } + + while (!IsListEmpty (&mPolicyListHead)) { + PolicyEntry = POLICY_ENTRY_FROM_LINK (mPolicyListHead.ForwardLink); + RemoveEntryList (&PolicyEntry->Link); + FreePool (PolicyEntry->Policy); + FreePool (PolicyEntry); + } + + ZeroMem (&Notifications[0], sizeof (Notifications)); + NotificationsCount = 0; + NotifyCountUpdateRemove = 0; +} + +/** + Callback for a policy notification event. This logs information about the + callback event for testing purposes. + + @param[in] PolicyGuid The GUID of the policy being notified. + @param[in] EventTypes The events that occurred for the notification. + @param[in] CallbackHandle The handle for the callback being invoked. +**/ +VOID +EFIAPI +GenericNotify ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT32 EventTypes, + IN VOID *CallbackHandle + ) +{ + POLICY_NOTIFY_ENTRY *Entry; + VOID *Policy; + EFI_STATUS Status; + + Entry = (POLICY_NOTIFY_ENTRY *)CallbackHandle; + ASSERT (Entry->Signature == POLICY_NOTIFY_ENTRY_SIGNATURE); + + Notifications[NotificationsCount].Guid = *PolicyGuid; + Notifications[NotificationsCount].Events = EventTypes; + Notifications[NotificationsCount].Priority = Entry->Priority; + NotificationsCount++; + + // + // For tests where we want to update the policy during a notification. First, + // remove this entry in the notification list to avoid infinite loops. + // + + if (NotifyCountUpdateRemove == NotificationsCount) { + Status = CommonUnregisterNotify (CallbackHandle); + ASSERT (!EFI_ERROR (Status)); + Policy = AllocatePool (10); + ASSERT (Policy != NULL); + Status = CommonSetPolicy (PolicyGuid, POLICY_ATTRIBUTE_FINALIZED, Policy, 10); + ASSERT (!EFI_ERROR (Status)); + } +} + +/** + Tests the basics of the notify callbacks. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +SimpleNotifyTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *Handle; + VOID *Policy; + EFI_GUID TestGuid0 = { + 0x93c7693d, 0xc072, 0x4b98, { 0xb1, 0x83, 0x18, 0x3d, 0xfe, 0xd5, 0x90, 0x5a } + }; + EFI_GUID TestGuid1 = { + 0xba63f0a3, 0x5058, 0x4daf, { 0x9e, 0x12, 0x54, 0x03, 0x81, 0xa1, 0x59, 0x6c } + }; + EFI_GUID TestGuid2 = { + 0xd122d807, 0xdacd, 0x4586, { 0x93, 0xa8, 0xa7, 0xc8, 0x99, 0x13, 0xe4, 0x82 } + }; + + // + // Setup a basic notify. + // + + Status = CommonRegisterNotify ( + &TestGuid0, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = CommonRegisterNotify ( + &TestGuid1, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = CommonRegisterNotify ( + &TestGuid2, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 0); + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = CommonSetPolicy (&TestGuid0, 0, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 1); + UT_ASSERT_MEM_EQUAL (&Notifications[0].Guid, &TestGuid0, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[0].Events, POLICY_NOTIFY_SET); + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = CommonSetPolicy (&TestGuid1, POLICY_ATTRIBUTE_FINALIZED, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 2); + UT_ASSERT_MEM_EQUAL (&Notifications[1].Guid, &TestGuid1, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[1].Events, POLICY_NOTIFY_SET | POLICY_NOTIFY_FINALIZED); + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = CommonSetPolicy (&TestGuid2, 0, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 3); + UT_ASSERT_MEM_EQUAL (&Notifications[2].Guid, &TestGuid2, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[2].Events, POLICY_NOTIFY_SET); + + // + // Remove a policy + // + + Status = CommonRemovePolicy (&TestGuid2); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 4); + UT_ASSERT_MEM_EQUAL (&Notifications[3].Guid, &TestGuid2, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[3].Events, POLICY_NOTIFY_REMOVED); + + return UNIT_TEST_PASSED; +} + +/** + Tests the priority mechanism of the notify callbacks. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +NotifyPriorityTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *Handle; + VOID *Policy; + EFI_GUID TestGuid = { + 0xf0192692, 0xd698, 0x4955, { 0xaf, 0xa4, 0xd5, 0xb0, 0xed, 0xa1, 0x38, 0x13 } + }; + + Status = CommonRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = CommonRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY - 1, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = CommonRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY + 1, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = CommonSetPolicy (&TestGuid, 0, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 3); + UT_ASSERT_MEM_EQUAL (&Notifications[0].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[1].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[2].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[0].Events, POLICY_NOTIFY_SET); + UT_ASSERT_EQUAL (Notifications[1].Events, POLICY_NOTIFY_SET); + UT_ASSERT_EQUAL (Notifications[2].Events, POLICY_NOTIFY_SET); + UT_ASSERT_EQUAL (Notifications[0].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY - 1); + UT_ASSERT_EQUAL (Notifications[1].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY); + UT_ASSERT_EQUAL (Notifications[2].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY + 1); + + return UNIT_TEST_PASSED; +} + +/** + Tests editing the policy in a callback. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +EditingNotifyTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *Handle; + VOID *Policy; + EFI_GUID TestGuid = { + 0xd71e9bc1, 0x56bf, 0x4ed1, { 0xaf, 0x1c, 0x52, 0x67, 0xf1, 0xfe, 0xed, 0xce } + }; + + Status = CommonRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = CommonRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY - 1, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = CommonRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY + 1, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + NotifyCountUpdateRemove = 2; + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = CommonSetPolicy (&TestGuid, 0, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 4); + UT_ASSERT_MEM_EQUAL (&Notifications[0].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[1].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[2].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[3].Guid, &TestGuid, sizeof (EFI_GUID)); + + UT_ASSERT_EQUAL (Notifications[0].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY - 1); + UT_ASSERT_EQUAL (Notifications[0].Events, POLICY_NOTIFY_SET); + + UT_ASSERT_EQUAL (Notifications[1].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY); + UT_ASSERT_EQUAL (Notifications[1].Events, POLICY_NOTIFY_SET); + + UT_ASSERT_EQUAL (Notifications[2].Events, POLICY_NOTIFY_SET | POLICY_NOTIFY_FINALIZED); + UT_ASSERT_EQUAL (Notifications[2].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY - 1); + + UT_ASSERT_EQUAL (Notifications[3].Events, POLICY_NOTIFY_SET | POLICY_NOTIFY_FINALIZED); + UT_ASSERT_EQUAL (Notifications[3].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY + 1); + + return UNIT_TEST_PASSED; +} + +/** + Initialize the unit test framework, suite, and unit tests for the + sample unit tests and run the unit tests. + + @retval EFI_SUCCESS All test cases were dispatched. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to + initialize the unit tests. +**/ +EFI_STATUS +EFIAPI +UefiTestMain ( + VOID + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE PolicyNotifyTests; + + Framework = NULL; + + DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_NAME, UNIT_TEST_VERSION)); + + // + // Start setting up the test framework for running the tests. + // + Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + // + // Populate the PolicyNotifyTests Unit Test Suite. + // + Status = CreateUnitTestSuite (&PolicyNotifyTests, Framework, "Policy Notification Tests", "DxeMmPolicy.Notify", NULL, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for PolicyNotifyTests\n")); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + AddTestCase (PolicyNotifyTests, "Test of basic policy notifications", "SimpleNotifyTest", SimpleNotifyTest, NULL, PolicyServiceCleanup, NULL); + AddTestCase (PolicyNotifyTests, "Test of policy priority", "NotifyPriorityTest", NotifyPriorityTest, NULL, PolicyServiceCleanup, NULL); + AddTestCase (PolicyNotifyTests, "Tests more complex use cases of notifications", "EditingNotifyTest", EditingNotifyTest, NULL, PolicyServiceCleanup, NULL); + + // + // Execute the tests. + // + Status = RunAllTestSuites (Framework); + +EXIT: + if (Framework) { + FreeUnitTestFramework (Framework); + } + + return Status; +} + +/** + Standard POSIX C entry point for host based unit test execution. +**/ +int +main ( + int argc, + char *argv[] + ) +{ + return UefiTestMain (); +} diff --git a/PolicyServicePkg/PolicyService/DxeMm/UnitTest/DxeMmPolicyUnitTest.inf b/PolicyServicePkg/PolicyService/DxeMm/UnitTest/DxeMmPolicyUnitTest.inf new file mode 100644 index 00000000000..23b5ef10923 --- /dev/null +++ b/PolicyServicePkg/PolicyService/DxeMm/UnitTest/DxeMmPolicyUnitTest.inf @@ -0,0 +1,33 @@ +## @file +# UnitTests for the DXE/MM policy service. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = DxeMmPolicyUnitTest + FILE_GUID = 5CC7D0F0-5B77-4C4C-84E8-95FEE089E342 + MODULE_TYPE = HOST_APPLICATION + VERSION_STRING = 1.0 + +[Sources] + DxeMmPolicyUnitTest.c + ../PolicyCommon.c + +[Packages] + MdePkg/MdePkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + UnitTestLib + HobLib + +[Guids] + gPolicyHobGuid + +[Pcd] diff --git a/PolicyServicePkg/PolicyService/Pei/PolicyPei.c b/PolicyServicePkg/PolicyService/Pei/PolicyPei.c new file mode 100644 index 00000000000..ffabe6264da --- /dev/null +++ b/PolicyServicePkg/PolicyService/Pei/PolicyPei.c @@ -0,0 +1,582 @@ +/** @file + Implements the protocol PPI, providing services to publish and access general policies in the PEI + environment. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PolicyPei.h" + +EFI_GUID gPeiPolicyNotifyListPpiGuid = { + 0x3f22d2a0, 0xb8f5, 0x47e4, { 0xb2, 0x84, 0x76, 0x5d, 0x2d, 0xbc, 0x40, 0xaf } +}; + +STATIC POLICY_PPI mPolicyPpi = { + PeiSetPolicy, + PeiGetPolicy, + PeiRemovePolicy, + PeiRegisterNotify, + PeiUnregisterNotify +}; + +STATIC EFI_PEI_PPI_DESCRIPTOR PolicyPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gPeiPolicyPpiGuid, + &mPolicyPpi +}; + +/** + Creates and emptry PPI for a given GUID to notify or dispatch consumers of + this policy GUID. If the PPI already exists it will be reinstalled. + + @param[in] PolicyGuid The policy GUID used for the PPI. + + @retval EFI_SUCCESS The PPI was installed or reinstalled. +**/ +EFI_STATUS +EFIAPI +PeiInstallPolicyIndicatorPpi ( + IN CONST EFI_GUID *PolicyGuid + ) +{ + EFI_STATUS Status; + EFI_PEI_PPI_DESCRIPTOR *PpiDescriptor; + EFI_PEI_PPI_DESCRIPTOR *OldPpiDescriptor; + EFI_GUID *AllocatedGuid; + VOID *ExistingPpi; + + Status = PeiServicesLocatePpi (PolicyGuid, 0, &OldPpiDescriptor, &ExistingPpi); + if (EFI_ERROR (Status)) { + OldPpiDescriptor = NULL; + } + + Status = PeiServicesAllocatePool (sizeof (EFI_PEI_PPI_DESCRIPTOR) + sizeof (EFI_GUID), (VOID **)&PpiDescriptor); + if (EFI_ERROR (Status)) { + return Status; + } + + AllocatedGuid = (EFI_GUID *)(PpiDescriptor + 1); + *AllocatedGuid = *PolicyGuid; + PpiDescriptor->Flags = EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST; + PpiDescriptor->Guid = AllocatedGuid; + PpiDescriptor->Ppi = NULL; + if (OldPpiDescriptor == NULL) { + Status = PeiServicesInstallPpi (PpiDescriptor); + } else { + Status = PeiServicesReInstallPpi (OldPpiDescriptor, PpiDescriptor); + } + + return Status; +} + +/** + Retrieves the hob for a given policy GUID. + + @param[in] PolicyGuid The policy GUID to match. + @param[out] PolicyHob Pointer to the policy hob header found. + + @retval EFI_SUCCESS The policy entry was found. + @retval EFI_NOT_FOUND The policy entry was not found. +**/ +EFI_STATUS +EFIAPI +PeiGetPolicyHob ( + IN CONST EFI_GUID *PolicyGuid, + OUT POLICY_HOB_HEADER **PolicyHob + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + POLICY_HOB_HEADER *CheckHob; + + GuidHob = GetFirstGuidHob (&gPolicyHobGuid); + while (GuidHob != NULL) { + CheckHob = (POLICY_HOB_HEADER *)GET_GUID_HOB_DATA (GuidHob); + if (!CheckHob->Removed && + CompareGuid (PolicyGuid, &CheckHob->PolicyGuid)) + { + *PolicyHob = CheckHob; + return EFI_SUCCESS; + } + + GuidHob = GetNextGuidHob (&gPolicyHobGuid, GET_NEXT_HOB (GuidHob)); + } + + return EFI_NOT_FOUND; +} + +/** + Checks if a given policy exists in the policy store. + + @param[in] PolicyGuid The policy GUID to match. + + @retval TRUE The policy exists in the store. + @retval FALSE The policy does not exists in the store. +**/ +BOOLEAN +EFIAPI +PeiCheckPolicyExists ( + IN CONST EFI_GUID *PolicyGuid + ) + +{ + POLICY_HOB_HEADER *PolicyHob; + + return !EFI_ERROR (PeiGetPolicyHob (PolicyGuid, &PolicyHob)); +} + +/** + Allocates a policy HOB and initialized its header structure. + + @param[in] PolicyGuid The policy GUID the HOB is created for. + @param[out] HobHeader The pointer to the created HOBs header structure. + + @retval EFI_SUCCESS The policy HOB was successfully allocated. + @retval EFI_BAD_BUFFER_SIZE The policy exceeds the maximum policy size. + @retval EFI_OUT_OF_RESOURCES Failed to create the GUID HOB. +**/ +EFI_STATUS +EFIAPI +PeiCreatePolicyHob ( + IN UINT16 PolicySize, + OUT POLICY_HOB_HEADER **HobHeader + ) +{ + POLICY_HOB_HEADER *HobPolicy; + UINT32 HobLength; + + HobLength = sizeof (POLICY_HOB_HEADER) + PolicySize; + if (HobLength > MAX_UINT16) { + DEBUG ((DEBUG_ERROR, "%a: Policy provided exceeds maximum HOB size! Required size: %lu\n", __FUNCTION__, HobLength)); + return EFI_BAD_BUFFER_SIZE; + } + + HobPolicy = BuildGuidHob (&gPolicyHobGuid, HobLength); + if (HobPolicy == NULL) { + DEBUG ((DEBUG_ERROR, "%a: Failed to create policy hob!\n", __FUNCTION__)); + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (HobPolicy, sizeof (POLICY_HOB_HEADER)); + HobPolicy->AllocationSize = PolicySize; + *HobHeader = HobPolicy; + return EFI_SUCCESS; +} + +/** + Retrieves the policy descriptor, buffer, and size for a given policy GUID. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + @param[out] Attributes The attributes of the stored policy. + @param[out] Policy The buffer where the policy data is copied. + @param[in,out] PolicySize The size of the stored policy data buffer. + On output, contains the size of the stored policy. + + @retval EFI_SUCCESS The policy was retrieved. + @retval EFI_BUFFER_TOO_SMALL The provided buffer size was too small. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +PeiGetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + OUT UINT64 *Attributes OPTIONAL, + OUT VOID *Policy, + IN OUT UINT16 *PolicySize + ) +{ + POLICY_HOB_HEADER *PolicyHob; + EFI_STATUS Status; + + if ((PolicyGuid == NULL) || + (PolicySize == NULL) || + ((Policy == NULL) && (*PolicySize != 0))) + { + return EFI_INVALID_PARAMETER; + } + + Status = PeiGetPolicyHob (PolicyGuid, &PolicyHob); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Attributes != NULL) { + *Attributes = PolicyHob->Attributes; + } + + if (*PolicySize < PolicyHob->PolicySize) { + *PolicySize = PolicyHob->PolicySize; + return EFI_BUFFER_TOO_SMALL; + } + + *PolicySize = PolicyHob->PolicySize; + CopyMem (Policy, GET_HOB_POLICY_DATA (PolicyHob), PolicyHob->PolicySize); + return EFI_SUCCESS; +} + +/** + Creates or updates a policy in the policy store. Will notify any applicable + callbacks. + + @param[in] PolicyGuid The uniquely identifying GUID for the policy. + @param[in] Attributes Attributes of the policy to be set. + @param[in] Policy The policy data buffer. This buffer will be + copied into the data store. + @param[in] PolicySize The size of the provided policy data. + + @retval EFI_SUCCESS Policy was created or updated. + @retval EFI_ACCESS_DENIED Policy was already finalized prior to this call. + @retval EFI_OUT_OF_RESOURCES Failed to allocate space for policy structures. +**/ +EFI_STATUS +EFIAPI +PeiSetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT64 Attributes, + IN VOID *Policy, + IN UINT16 PolicySize + ) + +{ + EFI_STATUS Status; + POLICY_HOB_HEADER *PolicyHob; + UINT32 Events; + + if ((PolicyGuid == NULL) || (Policy == NULL) || (PolicySize == 0)) { + return EFI_INVALID_PARAMETER; + } + + Status = PeiGetPolicyHob (PolicyGuid, &PolicyHob); + if (!EFI_ERROR (Status)) { + if (PolicyHob->Attributes & POLICY_ATTRIBUTE_FINALIZED) { + return EFI_ACCESS_DENIED; + } + + if (PolicySize <= PolicyHob->AllocationSize) { + PolicyHob->PolicySize = PolicySize; + PolicyHob->Attributes = Attributes; + CopyMem (GET_HOB_POLICY_DATA (PolicyHob), Policy, PolicySize); + Events = POLICY_NOTIFY_SET | (Attributes & POLICY_ATTRIBUTE_FINALIZED ? POLICY_NOTIFY_FINALIZED : 0); + PeiPolicyNotify (Events, PolicyHob); + if (Attributes & POLICY_ATTRIBUTE_FINALIZED ) { + Status = PeiInstallPolicyIndicatorPpi (PolicyGuid); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to install notify PPI. (%r)\n", __FUNCTION__, Status)); + return Status; + } + } + + return EFI_SUCCESS; + } + + // If the policy cannot fit in the existing buffer, mark it removed and + // allocate a new one. + PolicyHob->Removed = 1; + } + + Status = PeiCreatePolicyHob (PolicySize, &PolicyHob); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to create policy HOB. (%r)\n", __FUNCTION__, Status)); + return Status; + } + + PolicyHob->PolicyGuid = *PolicyGuid; + PolicyHob->PolicySize = PolicySize; + PolicyHob->Attributes = Attributes; + CopyMem (GET_HOB_POLICY_DATA (PolicyHob), Policy, PolicySize); + + Events = POLICY_NOTIFY_SET | (Attributes & POLICY_ATTRIBUTE_FINALIZED ? POLICY_NOTIFY_FINALIZED : 0); + PeiPolicyNotify (Events, PolicyHob); + + if (Attributes & POLICY_ATTRIBUTE_FINALIZED ) { + Status = PeiInstallPolicyIndicatorPpi (PolicyGuid); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to install notify PPI. (%r)\n", __FUNCTION__, Status)); + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Removes a policy from the policy store. The policy will be removed from the store + and freed if possible. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + + @retval EFI_SUCCESS The policy was removed. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +PeiRemovePolicy ( + IN CONST EFI_GUID *PolicyGuid + ) +{ + EFI_STATUS Status; + POLICY_HOB_HEADER *PolicyHob; + + if (PolicyGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = PeiGetPolicyHob (PolicyGuid, &PolicyHob); + if (!EFI_ERROR (Status)) { + PolicyHob->Removed = 1; + } + + PeiPolicyNotify (POLICY_NOTIFY_REMOVED, PolicyHob); + return Status; +} + +/** + Registers a callback for a policy event notification. The provided routine + will be invoked when one of multiple of the provided event types for the specified + guid occurs. + + @param[in] PolicyGuid The GUID of the policy the being watched. + @param[in] EventTypes The events to notify the callback for. + @param[in] Priority The priority of the callback where the lower values + will be called first. + @param[in] CallbackRoutine The function pointer of the callback to be invoked. + @param[out] Handle Returns the handle to this callback entry. + + @retval EFI_SUCCESS The callback notification as successfully registered. + @retval EFI_INVALID_PARAMETER EventTypes was 0 or Callback routine is invalid. + @retval Other The callback registration failed. +**/ +EFI_STATUS +EFIAPI +PeiRegisterNotify ( + IN CONST EFI_GUID *PolicyGuid, + IN CONST UINT32 EventTypes, + IN CONST UINT32 Priority, + IN POLICY_HANDLER_CALLBACK CallbackRoutine, + OUT VOID **Handle + ) +{ + EFI_HOB_GUID_TYPE *Hob; + PEI_POLICY_NOTIFY_HOB *NotifyList; + POLICY_NOTIFY_ENTRY *Entry; + UINT16 Index; + + if ((CallbackRoutine == NULL) || + (PolicyGuid == NULL) || + (Handle == NULL) || + ((EventTypes & POLICY_NOTIFY_ALL) != EventTypes)) + { + return EFI_INVALID_PARAMETER; + } + + // + // All notifications are tracked in HOBs. + // + + Index = 0; + Hob = GetFirstGuidHob (&gPolicyCallbackHobGuid); + while (Hob != NULL) { + NotifyList = GET_GUID_HOB_DATA (Hob); + if (NotifyList->Count < NOTIFY_ENTRIES_PER_HOB) { + break; + } + + Index++; + Hob = GetNextGuidHob (&gPolicyCallbackHobGuid, GET_NEXT_HOB (Hob)); + } + + if (Hob == NULL) { + NotifyList = (PEI_POLICY_NOTIFY_HOB *)BuildGuidHob (&gPolicyCallbackHobGuid, sizeof (PEI_POLICY_NOTIFY_HOB)); + if (NotifyList == NULL) { + DEBUG ((DEBUG_ERROR, "%a: Failed to allocate notification HOB.\n", __FUNCTION__)); + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (NotifyList, sizeof (PEI_POLICY_NOTIFY_HOB)); + NotifyList->Index = Index; + } + + Entry = &NotifyList->Entries[NotifyList->Count]; + Entry->PolicyGuid = *PolicyGuid; + Entry->EventTypes = EventTypes; + Entry->Priority = Priority; + Entry->CallbackRoutine = CallbackRoutine; + *Handle = NOTIFY_HANDLE (NotifyList->Index, NotifyList->Count); + NotifyList->Count++; + return EFI_SUCCESS; +} + +/** + Removes a registered notification callback. + + @param[in] Handle The handle for the registered callback. + + @retval EFI_SUCCESS The callback notification as successfully removed. + @retval EFI_INVALID_PARAMETER The provided handle is invalid. + @retval EFI_NOT_FOUND The provided handle could not be found. +**/ +EFI_STATUS +EFIAPI +PeiUnregisterNotify ( + IN VOID *Handle + ) +{ + EFI_STATUS Status; + EFI_HOB_GUID_TYPE *Hob; + PEI_POLICY_NOTIFY_HOB *NotifyList; + UINT16 HobIndex; + UINT16 EntryIndex; + + HobIndex = NOTIFY_HANDLE_HOB_INDEX (Handle); + EntryIndex = NOTIFY_HANDLE_ENTRY_INDEX (Handle); + + Status = EFI_NOT_FOUND; + Hob = GetFirstGuidHob (&gPolicyCallbackHobGuid); + while (Hob != NULL) { + NotifyList = GET_GUID_HOB_DATA (Hob); + if (NotifyList->Index == HobIndex) { + ASSERT (NotifyList->Count > EntryIndex); + ZeroMem (&NotifyList->Entries[EntryIndex], sizeof (POLICY_NOTIFY_ENTRY)); + NotifyList->Entries[EntryIndex].Tombstone = TRUE; + Status = EFI_SUCCESS; + break; + } + + Hob = GetNextGuidHob (&gPolicyCallbackHobGuid, GET_NEXT_HOB (Hob)); + } + + return Status; +} + +/** + Notifies all registered callbacks of a policy event. + + @param[in] EventTypes The event that occurred. + @param[in] PolicyHob The policy entry the event occurred for. +**/ +VOID +EFIAPI +PeiPolicyNotify ( + IN CONST UINT32 EventTypes, + IN POLICY_HOB_HEADER *PolicyHob + ) +{ + UINT16 Depth; + EFI_HOB_GUID_TYPE *Hob; + PEI_POLICY_NOTIFY_HOB *NotifyList; + UINT16 Index; + UINT32 LowestPriority; + UINT32 MinPriority; + BOOLEAN NotifiesFound; + + PolicyHob->NotifyDepth++; + Depth = PolicyHob->NotifyDepth; + + // + // This iteration method is very inefficient. The reasoning at the time of implementation + // is that PEI phase callbacks should be limited in quantity and so this inefficiency + // is worth it to avoid the complexity of keeping the HOB lists sorted. This iteration + // works in 2 passes. The first to find the next lowest priority and the second + // to invoke all notifications of that priority. + // + + MinPriority = 0; + while (TRUE) { + // + // First pass, find the next lowest priority. + // + + NotifiesFound = FALSE; + LowestPriority = MAX_UINT32; + Hob = GetFirstGuidHob (&gPolicyCallbackHobGuid); + while (Hob != NULL) { + NotifyList = GET_GUID_HOB_DATA (Hob); + for (Index = 0; Index < NotifyList->Count; Index++) { + if (!NotifyList->Entries[Index].Tombstone && + CompareGuid (&NotifyList->Entries[Index].PolicyGuid, &PolicyHob->PolicyGuid) && + ((NotifyList->Entries[Index].EventTypes & EventTypes) != 0) && + (NotifyList->Entries[Index].Priority >= MinPriority)) + { + NotifiesFound = TRUE; + if (NotifyList->Entries[Index].Priority < LowestPriority) { + LowestPriority = NotifyList->Entries[Index].Priority; + } + } + } + + Hob = GetNextGuidHob (&gPolicyCallbackHobGuid, GET_NEXT_HOB (Hob)); + } + + if (!NotifiesFound) { + break; + } + + // + // Second pass, call all of the notifies that match the priority. + // + Hob = GetFirstGuidHob (&gPolicyCallbackHobGuid); + while (Hob != NULL) { + NotifyList = GET_GUID_HOB_DATA (Hob); + for (Index = 0; Index < NotifyList->Count; Index++) { + if (!NotifyList->Entries[Index].Tombstone && + CompareGuid (&NotifyList->Entries[Index].PolicyGuid, &PolicyHob->PolicyGuid) && + ((NotifyList->Entries[Index].EventTypes & EventTypes) != 0) && + (NotifyList->Entries[Index].Priority == LowestPriority)) + { + NotifyList->Entries[Index].CallbackRoutine ( + &PolicyHob->PolicyGuid, + EventTypes, + NOTIFY_HANDLE (NotifyList->Index, Index) + ); + + // + // If a more recent notify went through then quick escape. + // + if (PolicyHob->NotifyDepth != Depth) { + goto LoopBreak; + } + } + } + + Hob = GetNextGuidHob (&gPolicyCallbackHobGuid, GET_NEXT_HOB (Hob)); + } + + if (LowestPriority == MAX_UINT32) { + break; + } + + MinPriority = LowestPriority + 1; + } + +LoopBreak: + + // + // If this is the top of the notification stack for this policy, then reset the + // tracking field. + // + + if (Depth == 1) { + PolicyHob->NotifyDepth = 0; + } +} + +/** + Entry point for the policy PEIM. Installs the policy service PPI. + + @param[in] FileHandle UNUSED. + @param[in] PeiServices UNUSED. + + @retval EFI_SUCCESS The interface was successfully installed. + @retval EFI_OUT_OF_RESOURCES There is no additional space in the PPI database. +**/ +EFI_STATUS +EFIAPI +PeiPolicyEntry ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + // Install the Policy PPI + return PeiServicesInstallPpi (&PolicyPpiList); +} diff --git a/PolicyServicePkg/PolicyService/Pei/PolicyPei.h b/PolicyServicePkg/PolicyService/Pei/PolicyPei.h new file mode 100644 index 00000000000..4ed02db5893 --- /dev/null +++ b/PolicyServicePkg/PolicyService/Pei/PolicyPei.h @@ -0,0 +1,166 @@ +/** @file + Prototypes and type definitions for the PEI Policy service + module. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _POLICY_PEI_H_ +#define _POLICY_PEI_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include "../PolicyHob.h" + +// +// Structures for tracking policy callbacks. +// + +#define NOTIFY_ENTRIES_PER_HOB (64) + +typedef struct _POLICY_NOTIFY_ENTRY { + EFI_GUID PolicyGuid; + UINT32 EventTypes; + UINT32 Priority; + POLICY_HANDLER_CALLBACK CallbackRoutine; + BOOLEAN Tombstone; +} POLICY_NOTIFY_ENTRY; + +typedef struct _PEI_POLICY_NOTIFY_HOB { + UINT16 Index; + UINT16 Count; + POLICY_NOTIFY_ENTRY Entries[NOTIFY_ENTRIES_PER_HOB]; +} PEI_POLICY_NOTIFY_HOB; + +// +// Pointers to HOBs should be avoided. For this reason, the PEI handle is +// actually just the index of the HOB and index in that HOB. +// + +#define NOTIFY_HANDLE(_hobindex, _entryindex) (VOID *)(UINTN)((_hobindex << 16) | _entryindex) +#define NOTIFY_HANDLE_HOB_INDEX(_handle) (UINT16)((((UINTN)_handle) >> 16) & MAX_UINT16) +#define NOTIFY_HANDLE_ENTRY_INDEX(_handle) (UINT16)((UINTN)_handle & MAX_UINT16) + +/** + Creates or updates a policy in the policy store. Will notify any applicable + callbacks. + + @param[in] PolicyGuid The uniquely identifying GUID for the policy. + @param[in] Attributes Attributes of the policy to be set. + @param[in] Policy The policy data buffer. This buffer will be + copied into the data store. + @param[in] PolicySize The size of the provided policy data. + + @retval EFI_SUCCESS Policy was created or updated. + @retval EFI_ACCESS_DENIED Policy was already finalized prior to this call. + @retval EFI_OUT_OF_RESOURCES Failed to allocate space for policy structures. +**/ +EFI_STATUS +EFIAPI +PeiSetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT64 Attributes, + IN VOID *Policy, + IN UINT16 PolicySize + ); + +/** + Retrieves the policy descriptor, buffer, and size for a given policy GUID. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + @param[out] Attributes The attributes of the stored policy. + @param[out] Policy The buffer where the policy data is copied. + @param[in,out] PolicySize The size of the stored policy data buffer. + On output, contains the size of the stored policy. + + @retval EFI_SUCCESS The policy was retrieved. + @retval EFI_BUFFER_TOO_SMALL The provided buffer size was too small. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +PeiGetPolicy ( + IN CONST EFI_GUID *PolicyGuid, + OUT UINT64 *Attributes OPTIONAL, + OUT VOID *Policy, + IN OUT UINT16 *PolicySize + ); + +/** + Removes a policy from the policy store. The policy will be removed from the store + and freed if possible. + + @param[in] PolicyGuid The GUID of the policy being retrieved. + + @retval EFI_SUCCESS The policy was removed. + @retval EFI_NOT_FOUND The policy does not exist. +**/ +EFI_STATUS +EFIAPI +PeiRemovePolicy ( + IN CONST EFI_GUID *PolicyGuid + ); + +/** + Registers a callback for a policy event notification. The provided routine + will be invoked when one of multiple of the provided event types for the specified + guid occurs. + + @param[in] PolicyGuid The GUID of the policy the being watched. + @param[in] EventTypes The events to notify the callback for. + @param[in] Priority The priority of the callback where the lower values + will be called first. + @param[in] CallbackRoutine The function pointer of the callback to be invoked. + @param[out] Handle Returns the handle to this callback entry. + + @retval EFI_SUCCESS The callback notification as successfully registered. + @retval EFI_INVALID_PARAMETER EventTypes was 0 or Callback routine is invalid. + @retval Other The callback registration failed. +**/ +EFI_STATUS +EFIAPI +PeiRegisterNotify ( + IN CONST EFI_GUID *PolicyGuid, + IN CONST UINT32 EventTypes, + IN CONST UINT32 Priority, + IN POLICY_HANDLER_CALLBACK CallbackRoutine, + OUT VOID **Handle + ); + +/** + Removes a registered notification callback. + + @param[in] Handle The handle for the registered callback. + + @retval EFI_SUCCESS The callback notification as successfully removed. + @retval EFI_INVALID_PARAMETER The provided handle is invalid. + @retval EFI_NOT_FOUND The provided handle could not be found. +**/ +EFI_STATUS +EFIAPI +PeiUnregisterNotify ( + IN VOID *Handle + ); + +/** + Notifies all registered callbacks of a policy event. + + @param[in] EventTypes The event that occurred. + @param[in] PolicyHob The policy entry the event occurred for. +**/ +VOID +EFIAPI +PeiPolicyNotify ( + IN CONST UINT32 EventTypes, + IN POLICY_HOB_HEADER *PolicyHob + ); + +#endif diff --git a/PolicyServicePkg/PolicyService/Pei/PolicyPei.inf b/PolicyServicePkg/PolicyService/Pei/PolicyPei.inf new file mode 100644 index 00000000000..cda9c547981 --- /dev/null +++ b/PolicyServicePkg/PolicyService/Pei/PolicyPei.inf @@ -0,0 +1,48 @@ +## @file +# +# This is a driver for PEI policy service module. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = PolicyPei + FILE_GUID = a124f01d-89f2-46c5-a4c0-da1630cec15c + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = PeiPolicyEntry + +[Sources] + PolicyPei.c + PolicyPei.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseMemoryLib + PeimEntryPoint + DebugLib + HobLib + PeiServicesLib + BaseLib + MemoryAllocationLib + +[Guids] + gPolicyHobGuid + gPolicyCallbackHobGuid + +[Ppis] + gPeiPolicyPpiGuid ## PRODUCES + +[FeaturePcd] + +[Pcd] + +[Depex] + TRUE diff --git a/PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTest.c b/PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTest.c new file mode 100644 index 00000000000..164a77042ad --- /dev/null +++ b/PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTest.c @@ -0,0 +1,469 @@ +/** @file + Host based unit tests for the PEI policy service module. + + Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UNIT_TEST_NAME "PEI Policy Service Unit Test" +#define UNIT_TEST_VERSION "1.0" + +// +// Internal tracking. +// + +typedef struct _NOTIFICATION_TRACKER { + EFI_GUID Guid; + UINT32 Events; + UINT32 Priority; +} NOTIFICATION_TRACKER; + +NOTIFICATION_TRACKER Notifications[150] = { 0 }; +UINT32 NotificationsCount = 0; +UINT32 NotifyCountUpdateRemove = 0; + +extern UINT32 HobByteIndex; +extern PEI_POLICY_NOTIFY_HOB *CurrentPolicyList; + +/** + Cleans up the policy and test state. + + @param[in] Context Unused. +**/ +VOID +EFIAPI +PolicyServiceCleanup ( + IN UNIT_TEST_CONTEXT Context + ) +{ + ZeroMem (&Notifications[0], sizeof (Notifications)); + NotificationsCount = 0; + NotifyCountUpdateRemove = 0; + HobByteIndex = 0; +} + +/** + Callback for a policy notification event. This logs information about the + callback event for testing purposes. + + @param[in] PolicyGuid The GUID of the policy being notified. + @param[in] EventTypes The events that occurred for the notification. + @param[in] CallbackHandle The handle for the callback being invoked. +**/ +VOID +EFIAPI +GenericNotify ( + IN CONST EFI_GUID *PolicyGuid, + IN UINT32 EventTypes, + IN VOID *CallbackHandle + ) +{ + VOID *Policy; + EFI_STATUS Status; + + ASSERT (CurrentPolicyList->Index == NOTIFY_HANDLE_HOB_INDEX (CallbackHandle)); + + Notifications[NotificationsCount].Guid = *PolicyGuid; + Notifications[NotificationsCount].Events = EventTypes; + Notifications[NotificationsCount].Priority = CurrentPolicyList->Entries[NOTIFY_HANDLE_ENTRY_INDEX (CallbackHandle)].Priority; + NotificationsCount++; + + // + // For tests where we want to update the policy during a notification. First, + // remove this entry in the notification list to avoid infinite loops. + // + + if (NotifyCountUpdateRemove == NotificationsCount) { + Status = PeiUnregisterNotify (CallbackHandle); + ASSERT (!EFI_ERROR (Status)); + Policy = AllocatePool (10); + ASSERT (Policy != NULL); + Status = PeiSetPolicy (PolicyGuid, POLICY_ATTRIBUTE_FINALIZED, Policy, 10); + ASSERT (!EFI_ERROR (Status)); + } +} + +/** + Tests the basics of the notify callbacks. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +SimpleNotifyTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *Handle; + VOID *Policy; + EFI_GUID TestGuid0 = { + 0x93c7693d, 0xc072, 0x4b98, { 0xb1, 0x83, 0x18, 0x3d, 0xfe, 0xd5, 0x90, 0x5a } + }; + EFI_GUID TestGuid1 = { + 0xba63f0a3, 0x5058, 0x4daf, { 0x9e, 0x12, 0x54, 0x03, 0x81, 0xa1, 0x59, 0x6c } + }; + EFI_GUID TestGuid2 = { + 0xd122d807, 0xdacd, 0x4586, { 0x93, 0xa8, 0xa7, 0xc8, 0x99, 0x13, 0xe4, 0x82 } + }; + + // + // Setup a basic notify. + // + + Status = PeiRegisterNotify ( + &TestGuid0, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = PeiRegisterNotify ( + &TestGuid1, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = PeiRegisterNotify ( + &TestGuid2, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 0); + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = PeiSetPolicy (&TestGuid0, 0, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 1); + UT_ASSERT_MEM_EQUAL (&Notifications[0].Guid, &TestGuid0, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[0].Events, POLICY_NOTIFY_SET); + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = PeiSetPolicy (&TestGuid1, POLICY_ATTRIBUTE_FINALIZED, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 2); + UT_ASSERT_MEM_EQUAL (&Notifications[1].Guid, &TestGuid1, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[1].Events, POLICY_NOTIFY_SET | POLICY_NOTIFY_FINALIZED); + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = PeiSetPolicy (&TestGuid2, 0, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 3); + UT_ASSERT_MEM_EQUAL (&Notifications[2].Guid, &TestGuid2, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[2].Events, POLICY_NOTIFY_SET); + + // + // Remove a policy + // + + Status = PeiRemovePolicy (&TestGuid2); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 4); + UT_ASSERT_MEM_EQUAL (&Notifications[3].Guid, &TestGuid2, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[3].Events, POLICY_NOTIFY_REMOVED); + + return UNIT_TEST_PASSED; +} + +/** + Tests the priority mechanism of the notify callbacks. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +NotifyPriorityTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *Handle; + VOID *Policy; + EFI_GUID TestGuid = { + 0xf0192692, 0xd698, 0x4955, { 0xaf, 0xa4, 0xd5, 0xb0, 0xed, 0xa1, 0x38, 0x13 } + }; + + Status = PeiRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = PeiRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY - 1, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = PeiRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY + 1, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = PeiSetPolicy (&TestGuid, 0, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 3); + UT_ASSERT_MEM_EQUAL (&Notifications[0].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[1].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[2].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[0].Events, POLICY_NOTIFY_SET); + UT_ASSERT_EQUAL (Notifications[1].Events, POLICY_NOTIFY_SET); + UT_ASSERT_EQUAL (Notifications[2].Events, POLICY_NOTIFY_SET); + UT_ASSERT_EQUAL (Notifications[0].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY - 1); + UT_ASSERT_EQUAL (Notifications[1].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY); + UT_ASSERT_EQUAL (Notifications[2].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY + 1); + + return UNIT_TEST_PASSED; +} + +/** + Tests editing the policy in a callback. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +EditingNotifyTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *Handle; + VOID *Policy; + EFI_GUID TestGuid = { + 0xd71e9bc1, 0x56bf, 0x4ed1, { 0xaf, 0x1c, 0x52, 0x67, 0xf1, 0xfe, 0xed, 0xce } + }; + + Status = PeiRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = PeiRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY - 1, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + Status = PeiRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + POLICY_NOTIFY_DEFAULT_PRIORITY + 1, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + NotifyCountUpdateRemove = 2; + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = PeiSetPolicy (&TestGuid, 0, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, 4); + UT_ASSERT_MEM_EQUAL (&Notifications[0].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[1].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[2].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_MEM_EQUAL (&Notifications[3].Guid, &TestGuid, sizeof (EFI_GUID)); + + UT_ASSERT_EQUAL (Notifications[0].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY - 1); + UT_ASSERT_EQUAL (Notifications[0].Events, POLICY_NOTIFY_SET); + + UT_ASSERT_EQUAL (Notifications[1].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY); + UT_ASSERT_EQUAL (Notifications[1].Events, POLICY_NOTIFY_SET); + + UT_ASSERT_EQUAL (Notifications[2].Events, POLICY_NOTIFY_SET | POLICY_NOTIFY_FINALIZED); + UT_ASSERT_EQUAL (Notifications[2].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY - 1); + + UT_ASSERT_EQUAL (Notifications[3].Events, POLICY_NOTIFY_SET | POLICY_NOTIFY_FINALIZED); + UT_ASSERT_EQUAL (Notifications[3].Priority, POLICY_NOTIFY_DEFAULT_PRIORITY + 1); + + return UNIT_TEST_PASSED; +} + +/** + Tests a large number of callbacks to test the multi-hob scenarios. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +LargeNumberNotifyTest ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *Handle; + VOID *Policy; + UINT32 Index; + CONST UINT32 Count = 140; + EFI_GUID TestGuid = { + 0x455d8ad7, 0xe730, 0x409b, { 0xbd, 0xaa, 0xcb, 0x52, 0xaf, 0x0a, 0x8b, 0x9e } + }; + + for (Index = 0; Index < Count; Index++) { + Status = PeiRegisterNotify ( + &TestGuid, + POLICY_NOTIFY_ALL, + Count - Index, + GenericNotify, + &Handle + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + } + + Policy = AllocatePool (10); + UT_ASSERT_NOT_NULL (Policy); + Status = PeiSetPolicy (&TestGuid, 0, Policy, 10); + UT_ASSERT_NOT_EFI_ERROR (Status); + + UT_ASSERT_EQUAL (NotificationsCount, Count); + for (Index = 0; Index < Count; Index++) { + UT_ASSERT_MEM_EQUAL (&Notifications[Index].Guid, &TestGuid, sizeof (EFI_GUID)); + UT_ASSERT_EQUAL (Notifications[Index].Priority, Index + 1); + UT_ASSERT_EQUAL (Notifications[Index].Events, POLICY_NOTIFY_SET); + } + + return UNIT_TEST_PASSED; +} + +/** + Initialize the unit test framework, suite, and unit tests for the + sample unit tests and run the unit tests. + + @retval EFI_SUCCESS All test cases were dispatched. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to + initialize the unit tests. +**/ +EFI_STATUS +EFIAPI +UefiTestMain ( + VOID + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE PolicyNotifyTests; + + Framework = NULL; + + DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_NAME, UNIT_TEST_VERSION)); + + // + // Start setting up the test framework for running the tests. + // + Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + // + // Populate the PolicyNotifyTests Unit Test Suite. + // + Status = CreateUnitTestSuite (&PolicyNotifyTests, Framework, "Policy Notification Tests", "PeiPolicy.Notify", NULL, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for PolicyNotifyTests\n")); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + AddTestCase (PolicyNotifyTests, "Test of basic policy notifications", "SimpleNotifyTest", SimpleNotifyTest, NULL, PolicyServiceCleanup, NULL); + AddTestCase (PolicyNotifyTests, "Test of policy priority", "NotifyPriorityTest", NotifyPriorityTest, NULL, PolicyServiceCleanup, NULL); + AddTestCase (PolicyNotifyTests, "Tests more complex use cases of notifications", "EditingNotifyTest", EditingNotifyTest, NULL, PolicyServiceCleanup, NULL); + AddTestCase (PolicyNotifyTests, "Tests a large number of notifications", "LargeNumberNotifyTest", LargeNumberNotifyTest, NULL, PolicyServiceCleanup, NULL); + + // + // Execute the tests. + // + Status = RunAllTestSuites (Framework); + +EXIT: + if (Framework) { + FreeUnitTestFramework (Framework); + } + + return Status; +} + +/** + Standard POSIX C entry point for host based unit test execution. +**/ +int +main ( + int argc, + char *argv[] + ) +{ + return UefiTestMain (); +} diff --git a/PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTest.inf b/PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTest.inf new file mode 100644 index 00000000000..3f0bff38348 --- /dev/null +++ b/PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTest.inf @@ -0,0 +1,37 @@ +## @file +# UnitTests for the PEI policy service. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = PeiPolicyUnitTest + FILE_GUID = 96A4234C-7D6E-4321-A3AB-77FCEE7B972A + MODULE_TYPE = HOST_APPLICATION + VERSION_STRING = 1.0 + +[Sources] + PeiPolicyUnitTest.c + PeiPolicyUnitTestMocks.c + ../PolicyPei.c + +[Packages] + MdePkg/MdePkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + UnitTestLib + +[Guids] + gPolicyHobGuid + gPolicyCallbackHobGuid + +[Ppis] + gPeiPolicyPpiGuid + +[Pcd] diff --git a/PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTestMocks.c b/PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTestMocks.c new file mode 100644 index 00000000000..f8dd8fb8327 --- /dev/null +++ b/PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTestMocks.c @@ -0,0 +1,130 @@ +/** @file + Mock implementations for PeiPolicyUnitTest + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +// +// HOB mocking, this needs to actually work for this test work operate. This is +// implemented as a super simple hob list, supporting only GUID hobs. +// + +#define HOB_BUFFER_SIZE (10 * 0x1000) + +UINTN HobByteIndex = 0; +UINT8 HobBuffer[HOB_BUFFER_SIZE]; +PEI_POLICY_NOTIFY_HOB *CurrentPolicyList; + +VOID * +EFIAPI +GetNextGuidHob ( + IN CONST EFI_GUID *Guid, + IN CONST VOID *HobStart + ) +{ + EFI_HOB_GUID_TYPE *Hob; + + Hob = (EFI_HOB_GUID_TYPE *)HobStart; + while ((UINT8 *)Hob < &HobBuffer[HobByteIndex]) { + ASSERT (Hob->Header.HobType == EFI_HOB_TYPE_GUID_EXTENSION); + if (CompareGuid (&Hob->Name, Guid)) { + if (CompareGuid (Guid, &gPolicyCallbackHobGuid)) { + CurrentPolicyList = GET_GUID_HOB_DATA (Hob); + } + + return Hob; + } + + Hob = GET_NEXT_HOB (Hob); + } + + return NULL; +} + +VOID * +EFIAPI +GetFirstGuidHob ( + IN CONST EFI_GUID *Guid + ) +{ + return GetNextGuidHob (Guid, &HobBuffer[0]); +} + +VOID * +EFIAPI +BuildGuidHob ( + IN CONST EFI_GUID *Guid, + IN UINTN DataLength + ) +{ + EFI_HOB_GUID_TYPE *Hob; + + ASSERT (HobByteIndex + sizeof (EFI_HOB_GUID_TYPE) + DataLength < HOB_BUFFER_SIZE); + + ZeroMem (&HobBuffer[HobByteIndex], sizeof (EFI_HOB_GUID_TYPE) + DataLength); + Hob = (EFI_HOB_GUID_TYPE *)&HobBuffer[HobByteIndex]; + Hob->Header.HobType = EFI_HOB_TYPE_GUID_EXTENSION; + Hob->Header.HobLength = (UINT16)(sizeof (EFI_HOB_GUID_TYPE) + DataLength); + Hob->Name = *Guid; + HobByteIndex += (sizeof (EFI_HOB_GUID_TYPE) + DataLength); + if (CompareGuid (Guid, &gPolicyCallbackHobGuid)) { + CurrentPolicyList = GET_GUID_HOB_DATA (Hob); + } + + return GET_GUID_HOB_DATA (Hob); +} + +// +// Other functions. +// + +EFI_STATUS +EFIAPI +PeiServicesAllocatePool ( + IN UINTN Size, + OUT VOID **Buffer + ) +{ + ASSERT (Buffer != NULL); + *Buffer = AllocatePool (Size); + ASSERT (*Buffer != NULL); + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +PeiServicesInstallPpi ( + IN CONST EFI_PEI_PPI_DESCRIPTOR *PpiList + ) +{ + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +PeiServicesReInstallPpi ( + IN CONST EFI_PEI_PPI_DESCRIPTOR *OldPpi, + IN CONST EFI_PEI_PPI_DESCRIPTOR *NewPpi + ) +{ + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +PeiServicesLocatePpi ( + IN CONST EFI_GUID *Guid, + IN UINTN Instance, + IN OUT EFI_PEI_PPI_DESCRIPTOR **PpiDescriptor OPTIONAL, + IN OUT VOID **Ppi + ) +{ + return EFI_NOT_FOUND; +} diff --git a/PolicyServicePkg/PolicyService/PolicyHob.h b/PolicyServicePkg/PolicyService/PolicyHob.h new file mode 100644 index 00000000000..e9fb8f3c527 --- /dev/null +++ b/PolicyServicePkg/PolicyService/PolicyHob.h @@ -0,0 +1,24 @@ +/** @file + Common private definitions used by the policy service modules. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _POLICY_HOB_H_ +#define _POLICY_HOB_H_ + +typedef struct _POLICY_HOB_HEADER { + EFI_GUID PolicyGuid; + UINT64 Attributes; + UINT16 PolicySize; + UINT16 AllocationSize; + UINT16 Removed : 1; + UINT16 Reserved : 15; + UINT16 NotifyDepth; // PEI Only +} POLICY_HOB_HEADER; + +#define GET_HOB_POLICY_DATA(_hob_header) ((VOID *)(((UINT8*)_hob_header) + sizeof(POLICY_HOB_HEADER))) + +#endif diff --git a/PolicyServicePkg/PolicyServicePkg.ci.yaml b/PolicyServicePkg/PolicyServicePkg.ci.yaml new file mode 100644 index 00000000000..595743f8e81 --- /dev/null +++ b/PolicyServicePkg/PolicyServicePkg.ci.yaml @@ -0,0 +1,73 @@ +## @file +# CI configuration for PolicServicePkg +# +# Copyright (c) Microsoft Corporation +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +{ + "LicenseCheck": { + "IgnoreFiles": [] + }, + "EccCheck": { + ## Exception sample looks like below: + ## "ExceptionList": [ + ## "", "" + ## ] + "ExceptionList": [ + ], + ## Both file path and directory path are accepted. + "IgnoreFiles": [ + ] + }, + "CompilerPlugin": { + "DscPath": "PolicyServicePkg.dsc" + }, + "HostUnitTestCompilerPlugin": { + "DscPath": "Test/PolicyServicePkgHostTest.dsc" + }, + "CharEncodingCheck": { + "IgnoreFiles": [] + }, + "DependencyCheck": { + "AcceptableDependencies": [ + "MdePkg/MdePkg.dec", + "MdeModulePkg/MdeModulePkg.dec", + "PolicyServicePkg/PolicyServicePkg.dec", + ], + # For host based unit tests + "AcceptableDependencies-HOST_APPLICATION":[ + "UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec" + ], + # For UEFI shell based apps + "AcceptableDependencies-UEFI_APPLICATION":[], + "IgnoreInf": [] + }, + "DscCompleteCheck": { + "DscPath": "PolicyServicePkg.dsc", + "IgnoreInf": [] + }, + "HostUnitTestDscCompleteCheck": { + "IgnoreInf": [], + "DscPath": "Test/PolicyServicePkgHostTest.dsc" + }, + "GuidCheck": { + "IgnoreGuidName": [], + "IgnoreGuidValue": [], + "IgnoreFoldersAndFiles": [], + "IgnoreDuplicates": [] + }, + "LibraryClassCheck": { + "IgnoreHeaderFile": [] + }, + "SpellCheck": { + "AuditOnly": False, + "ExtendWords": [], # words to extend to the dictionary for this package + "IgnoreStandardPaths": [], # Standard Plugin defined paths that should be ignore + "AdditionalIncludePaths": [] # Additional paths to spell check (wildcards supported) + }, + "MarkdownLintCheck": { + "IgnoreFiles": [ + ] # package root relative file, folder, or glob pattern to ignore + } +} diff --git a/PolicyServicePkg/PolicyServicePkg.dec b/PolicyServicePkg/PolicyServicePkg.dec new file mode 100644 index 00000000000..34a6c6e6c0a --- /dev/null +++ b/PolicyServicePkg/PolicyServicePkg.dec @@ -0,0 +1,33 @@ +## @file +# This package contains the policy service and related libraries +# and headers. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + DEC_SPECIFICATION = 0x00010017 + PACKAGE_NAME = PolicyServicePkg + PACKAGE_GUID = EE01A914-32E5-45AA-AC39-AAA4E77EE10C + PACKAGE_VERSION = 0.1 + +[Includes] + Include + +[LibraryClasses] + PolicyLib|Include/Library/PolicyLib.h + +[Guids] + ## GUID used for storing policy data in HOB. + gPolicyHobGuid = { 0x455f8b79, 0x6713, 0x4119, { 0x8a, 0x9c, 0x88, 0x7d, 0x1a, 0x33, 0xe2, 0x43 } } + gPolicyCallbackHobGuid = { 0xdcf916b7, 0xec13, 0x4d14, {0xba, 0x3b, 0xac, 0x7e, 0x2d, 0xea, 0xe1, 0x7c } } + +[Ppis] + ## Include/Ppi/Policy.h + gPeiPolicyPpiGuid = { 0xa8b33630, 0xa1ae, 0x4e2d, { 0x8d, 0x0f, 0x3d, 0xf3, 0xe5, 0x87, 0x08, 0xce } } + +[Protocols] + ## Include/Protocol/Policy.h + gPolicyProtocolGuid = { 0xd7c9b744, 0x13a5, 0x4377, { 0x8d, 0x2a, 0x6b, 0x37, 0xad, 0x1f, 0xd8, 0x2a } } + gMmPolicyProtocolGuid = { 0xe55ad3a1, 0xbd34, 0x46f4, { 0xbb, 0x6e, 0x72, 0x28, 0x0b, 0xdc, 0xbf, 0xd9 } } diff --git a/PolicyServicePkg/PolicyServicePkg.dsc b/PolicyServicePkg/PolicyServicePkg.dsc new file mode 100644 index 00000000000..8aae8d9a6e3 --- /dev/null +++ b/PolicyServicePkg/PolicyServicePkg.dsc @@ -0,0 +1,74 @@ +## @file +# Policy Service Platform. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + PLATFORM_NAME = PolicyService + PLATFORM_GUID = 237CCD9C-7B39-43D9-B346-58432620A05B + PLATFORM_VERSION = 1.0 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/PolicyService + SUPPORTED_ARCHITECTURES = IA32|X64|EBC|ARM|AARCH64|RISCV64 + BUILD_TARGETS = DEBUG|RELEASE|NOOPT + SKUID_IDENTIFIER = DEFAULT + +[LibraryClasses.common] + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf + RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf + + # Libraries used for test modules. + UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf + UnitTestPersistenceLib|UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf + UnitTestResultReportLib|UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf + +[LibraryClasses.common.PEIM] + MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf + HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf + PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf + PolicyLib|PolicyServicePkg/Library/PeiPolicyLib/PeiPolicyLib.inf + PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf + +[LibraryClasses.IA32.PEIM, LibraryClasses.X64.PEIM] + PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLibIdt/PeiServicesTablePointerLibIdt.inf + +[LibraryClasses.common.DXE_DRIVER] + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf + PolicyLib|PolicyServicePkg/Library/DxePolicyLib/DxePolicyLib.inf + +[LibraryClasses.common.MM_STANDALONE] + MemoryAllocationLib|StandaloneMmPkg/Library/StandaloneMmMemoryAllocationLib/StandaloneMmMemoryAllocationLib.inf + StandaloneMmDriverEntryPoint|MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf + HobLib|StandaloneMmPkg/Library/StandaloneMmHobLib/StandaloneMmHobLib.inf + MmServicesTableLib|MdePkg/Library/StandaloneMmServicesTableLib/StandaloneMmServicesTableLib.inf + StandaloneMmDriverEntryPoint|MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf + PolicyLib|PolicyServicePkg/Library/MmPolicyLib/MmPolicyLib.inf + +[Components] + PolicyServicePkg/PolicyService/DxeMm/PolicyDxe.inf + PolicyServicePkg/PolicyService/DxeMm/PolicyMm.inf + PolicyServicePkg/PolicyService/Pei/PolicyPei.inf + PolicyServicePkg/Library/DxePolicyLib/DxePolicyLib.inf + PolicyServicePkg/Library/PeiPolicyLib/PeiPolicyLib.inf + PolicyServicePkg/Library/MmPolicyLib/MmPolicyLib.inf + + # Sample and test modules. + PolicyServicePkg/Samples/PolicyInterface/PolicySampleDxe.inf + PolicyServicePkg/Samples/PolicyInterface/PolicySamplePei.inf + PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestDxe.inf + PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestPei.inf + PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestMm.inf diff --git a/PolicyServicePkg/README.md b/PolicyServicePkg/README.md new file mode 100644 index 00000000000..cf9f989e58d --- /dev/null +++ b/PolicyServicePkg/README.md @@ -0,0 +1,224 @@ +# Policy Service + +Documents the interface, design, and implementation of the Policy service split +across PEI and DXE modules as well as any supporting libraries or best +practices. + +## Overview + +The generalized policy service, implemented in DXE and PEI, provides interfaces +for components to publish and consume generic policies between components in the +UEFI environment. This policy takes the form of generic data and it is up to the +producer and consumer to agree upon the contents and format of that policy. This +document makes recommendations as to best practices to sustainably format policy +data, but the service is agnostic to the format or meaning of the policy. + +Policy is a data block containing configurations or settings relating to the +silicon, platform, or feature state of the system. This information can +originate from PCD entries, platform/silicon configuration code, user settings, +or otherwise determined at runtime. A policy may be entirely defined by a single +component or it can be built and transformed by several components each further +customizing or locking down the system. + +## Background + +In UEFI there is a need to share these Policy data blocks across components +or phases. This could be specific device configuration, settings data, +platform information, etc. The Policy service is intended to give an easy, +abstracted, and extensible interface for accomplishing this data publishing. + +### Previous Technologies + +There are several existing mechanisms that can be used to share configuration +and data with the rest of the UEFI environment, but currently these mechanisms +either don't perfectly align with the Policy Service's use case or are not +suitable for adoption across all platforms. Below is a comparison to some of the +currently existing methods for sharing data blocks in UEFI. + +_NVRAM Variables_ - Because NVRAM will be implemented at the platform level, +leveraging across silicon or other base code causes a inverse dependency. + +_Hand Off Blocks_ - While handoff blocks can be used to share data, they only +solve the particular case from where the data is sourced from PEI. HOBs do not +provide any robust data management features. Additionally, HOBs can be easily +misused, for example by storing direct references to the HOB across memory +initialization. + +_Config Blocks_ - Config blocks serve a similar use case as the Policy service, +but is not currently in a position to be used across the UEFI ecosystem. Config +Blocks lack some of the features such as data management abilities, or +features such as the ability to dispatch and notify on data updates. +Additionally Config Blocks lack public documentation to be widely adopted. + +_Platform Configuration Database_ - PCDs, in particular dynamic PCDs, can also +be used to share data across components. However, PCDs are more focused and +prescriptive in the data type and use case while also lacking some of the data +management features and extensibility. Due to it's dependence on the build +system PCDs can also be problematic when integrating pre-compiled binary +components. The Policy Service is not intended to replace PCDs, but to provide a +more light weight and simple method for components to share data. + +## Data Format + +The Policy service only implements a generic blob storage and should not be +directly used in many scenarios. For managed configuration and data format, see +the [Policy Library ReadMe](./Library/README.md). + +## Policy Management Process + +Policies are uniquely defined by a GUID. This GUID should only be used for a +specific data type and purpose. Only one policy may be published under a +specific GUID at any given time. Duplicates will overwrite the original policy +unless the policy has been finalized via the policy attributes. Policies are +managed through a simple Get/Set/Remove interface detailed below. After their +creation, policies are universally available to all components and supported +environments unless otherwise specified in the policy attributes. + +### Policy Components + +The policy will be accessed by two types of components: producers and consumers. +There may be some components that are both consumers and producers by altering +an existing policy, possibly using notifications to alter the policy as soon as +it is made available. + + Producer - Creates original policy + |--->[Consumer/Producer - Alters policy] + |--->Consumer 1 - Reads altered policy + |--->Consumer 2 - Reads altered Policy + +The original policy will often be created in PEI to be available as soon as +possible, and may be consumed in PEI or may not be consumed until DXE. +Implementors of a new policy should consider what component is the origin of the +the data, what components may need to edit the data, and what components will +eventually use the data. In the sample code, the [PEI sample module](Samples\PolicyInterface\PolicySamplePei.c) +is the producer and the [DXE sample module](Samples\PolicyInterface\PolicySampleDxe.c) +is the consumer for the example policy shared from PEI to DXE. + +Producers may also finalize the policy to prevent future accessors from writing +to the data. Finalizing will prevent specialization of a policy and so should be +done sparingly in silicon or other low-level components. + +### Attributes + +Policies can be can be set with attribute flags allowing the policy provider to +specify on how the policy is handled. For example, this can be used to finalize +the policy making it read-only or can be used to limit access to the policy to +a phase of boot. See the PolicyInterface header definitions for full list of +attributes. + +## Policy Interface + +Both the PEI and DXE implementation provide the following interfaces. + +### _SetPolicy_ + +Creates a new or overwrites and existing policy. Policies can only be +overwritten if the policy has not be finalized. The policy will be copied from +the provided buffer to an internal store, so all further edits must be done +though additional calls to _SetPolicy_. + +### _GetPolicy_ + +Returns a copy of the policy for the provided policy GUID and its current +attributes. The caller is responsible for allocating the buffer the policy is +copied into. + +### _RemovePolicy_ + +Removes a policy from the policy list, freeing it when possible. + +### _RegisterNotify_ + +Registers a callback that will be invoked when a policy is edited in some way. The +reasons for the callback can be filtered through the EventTypes parameter. + +### _UnregisterNotify_ + +Removes a policy notification callback, prevent any further invocation. + +## Policy Notifications + +Consumers may subscribe to policy updates in multiple ways. For end-consumers +of a policy they may use the policy GUID in their `DEPEX` for the signal final policy, +and all other consumers may use the policy notification callbacks. + +### Final Policy Signal Protocol & PPI + +When a policy is finalized a PPI or Protocol will be installed with the +GUID of the policy. Consumers may use this GUID to either set Protocol/PPI +notifications, or create a DEPEX dependency so that the consumer is not +dispatched until the final policy is made available. The protocol/PPI will not +contain any useful interface and consumers are expected to use the Policy Service protocol +interface to retrieve the policy data after being notified or dispatched. + +### Policy Service Callbacks + +The policy service supports callbacks for various types of policy events such +as a policy being created, updated, finalized, or removed. Consumers may use the +`RegisterNotify` routine to set a callback in the event that a provided policy +undergoes the event specified in the callback registration. A given callbacks may +receive an event with multiple events at once. For example, a finalized event will +always be accompanied by a set event since a policy must be set to be finalized. + +Registered callbacks will be invoked in order of ascending `Priority` with ties +being resolved with a first-come-first-serve approach. Callbacks may make +edits to the underlying policy which will result in all of the original still +pending notifications being canceled and the all of callbacks being re-invoked +with the new event. For example, if you have the notifications of ascending priority +where B edits the policy the sequence of callbacks would be + + A -> B (EDIT) -> A -> B -> C + +For this reason, callbacks that edit the policy should either remove their +notification first or take precautions to not create a infinite notification loop. + +## Policy Service Implementation + +The policy interfaces use a pass-by-copy scheme to ensure that the producers and +consumers cannot directly edit the policy data and all interactions are done in +transactions. This also allows for attributes such as _finalized_ to be strongly +enforced. Internally the PEI and DXE phase implementation have their own method +for storing policy data. + +### PEI Phase + +Policies created during PEI are immediately stored into a HOB. All policy HOBs +are given the same well-known GUID, gPolicyHobGuid, with a POLICY_HOB_HEADER +header to track it's metadata. When a given policy is requested the PEIM will +search the HOBs with the well-known GUID to find the matching policy header. If +a policy is removed or made obsolete with a larger version of the policy, the +header will be set with the removed flag and will no longer be evaluated by the +service. + +When a policy is created or updated in the PEI phase, a NULL PPI will be +installed, or reinstalled, with the GUID of the policy. This PPI is intended to +allow for notification and dispatch of consumers when the policy becomes +available. + +### DXE Phase + +During it's initialization, the DXE driver will process the HOB list to discover +any valid policies that may exist. These HOB policy blocks will then be added to +the DXE driver's linked list structure of the active policies on the system. Any +policies created or updated in the DXE phase will be allocated in pool memory +and freed when removed or expanded. + +Like the PEIM, the DXE driver will install/reinstall a NULL protocol with the +given policies GUID when it is created or updated to allow for notification and +dispatch on the policy availability. + +### Standalone MM + +Like the DXE phase, the MM policy service will ingest any policies from the HOB +list for architectures. The MM policy service is implemented identically to the +DXE phase module with the exception of using the MM specific protocols. + +Policies in the MM service are isolated from PEI and DXE. The MM module will ingest +any policies available in the hob list from PEI where applicable but no policies +created or modified by PEI or DXE after Standalone MM has been launched will be +available from the MM policy service. Similarly, no policy created or edited in +the MM policy service will be readable from the PEI or DXE policies services. + +Policies are not shared with the standalone MM module after initialization. Any +policy created in MM will not be readable by DXE and PEI, and any policy made after +MM initialization will not be readable from MM. diff --git a/PolicyServicePkg/Samples/PolicyInterface/PolicySampleDxe.c b/PolicyServicePkg/Samples/PolicyInterface/PolicySampleDxe.c new file mode 100644 index 00000000000..855d48d9259 --- /dev/null +++ b/PolicyServicePkg/Samples/PolicyInterface/PolicySampleDxe.c @@ -0,0 +1,115 @@ +/** @file + Implements sample policy for DXE environment. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +#include +#include "SamplePolicy.h" + +// +// Guids used to store sample policies. For production scenarios this should be +// defined in appropriate .dec file. +// + +EFI_GUID mSampleGuidPeiToDxe = POLICY_SAMPLE_PEI_TO_DXE_GUID; + +// +// Global to store the protocol. +// +POLICY_PROTOCOL *mPolicyProtocol; + +/** + A routine to retrieve the sample policy created by the PEI sample module. + + @param[in,out] PolicyGuid The GUID to use for the sample policy. + + @retval EFI_SUCCESS Successfully ran sample policy code. + @retval EFI_PROTOCOL_ERROR Unexpected status returned by policy interface. + @retval other Failure status returned by policy interface. +**/ +EFI_STATUS +DxeSampleGetPeiPolicy ( + IN EFI_GUID *PolicyGuid + ) +{ + EFI_STATUS Status; + SAMPLE_POLICY Policy; + UINT16 PolicySize; + UINT64 Attributes; + + // Set the policy size to 0 to indicate a null policy pointer. + PolicySize = 0; + + // First check the size. This would usually be done for policies of a dynamic + // or changing size. Attributes may be retrieved at this time if desired. + Status = mPolicyProtocol->GetPolicy (PolicyGuid, NULL, NULL, &PolicySize); + if ((Status != EFI_BUFFER_TOO_SMALL) || (PolicySize != sizeof (Policy))) { + ASSERT (FALSE); + return EFI_PROTOCOL_ERROR; + } + + // Retrieve the actual policy. + Status = mPolicyProtocol->GetPolicy (PolicyGuid, &Attributes, &Policy, &PolicySize); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // Verify all the details are correct. + ASSERT (PolicySize == sizeof (Policy)); + ASSERT (Attributes == 0); + ASSERT (Policy.Signature == SAMPLE_POLICY_SIGNATURE); + ASSERT (Policy.Revision == SAMPLE_POLICY_REVISION); + ASSERT (Policy.Value == SAMPLE_POLICY_VALUE); + + return Status; +} + +/** + DXE policy driver entry point. Initialized the policy store from the HOB list + and install the DXE policy protocol. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable UNUSED. + + @retval EFI_SUCCESS Policy store initialized and protocol installed. + @retval other Sample routines returned a failure. +**/ +EFI_STATUS +EFIAPI +DxePolicySampleEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // Get the policy protocol. + Status = gBS->LocateProtocol ( + &gPolicyProtocolGuid, + NULL, + (VOID **)&mPolicyProtocol + ); + + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + Status = DxeSampleGetPeiPolicy (&mSampleGuidPeiToDxe); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + return Status; +} diff --git a/PolicyServicePkg/Samples/PolicyInterface/PolicySampleDxe.inf b/PolicyServicePkg/Samples/PolicyInterface/PolicySampleDxe.inf new file mode 100644 index 00000000000..481de8388ba --- /dev/null +++ b/PolicyServicePkg/Samples/PolicyInterface/PolicySampleDxe.inf @@ -0,0 +1,38 @@ +## @file +# +# This is a sample DXE driver for using the policy service. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = PolicySampleDxe + FILE_GUID = CDBFC5F3-4C3B-467F-92AA-5E2F2FEEA106 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DxePolicySampleEntry + +[Sources] + PolicySampleDxe.c + SamplePolicy.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseLib + UefiDriverEntryPoint + DebugLib + UefiRuntimeServicesTableLib + +[Protocols] + gPolicyProtocolGuid ## CONSUMES + +[Depex] + gPolicyProtocolGuid + diff --git a/PolicyServicePkg/Samples/PolicyInterface/PolicySamplePei.c b/PolicyServicePkg/Samples/PolicyInterface/PolicySamplePei.c new file mode 100644 index 00000000000..7c7f2fd3362 --- /dev/null +++ b/PolicyServicePkg/Samples/PolicyInterface/PolicySamplePei.c @@ -0,0 +1,171 @@ +/** @file + Implements sample policy for PEI environment. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include +#include "SamplePolicy.h" + +// +// Guids used to store sample policies. For production scenarios this should be +// defined in appropriate .dec file. +// + +EFI_GUID mSamplePolicyGuid1 = { + 0xbb7b94d5, 0x105c, 0x41a6, { 0xb4, 0xe4, 0xa1, 0x58, 0x2e, 0xde, 0xf0, 0x7e } +}; + +EFI_GUID mSampleGuidPeiToDxe = POLICY_SAMPLE_PEI_TO_DXE_GUID; + +// +// Global to simplify examples. +// + +POLICY_PPI *mPolicyPpi; + +/** + A routine to sample the different Policy PPI functions. This routine will not + leave any active policies. + + @param[in,out] PolicyGuid The GUID to use for the sample policy. + + @retval EFI_SUCCESS Successfully ran sample policy code. + @retval EFI_PROTOCOL_ERROR Unexpected status returned by policy interface. + @retval other Failure status returned by policy interface. +**/ +EFI_STATUS +PeiSampleSetGetRemovePolicy ( + IN EFI_GUID *PolicyGuid + ) +{ + SAMPLE_POLICY Policy; + SAMPLE_POLICY ResultPolicy; + UINT64 Attributes; + EFI_STATUS Status; + UINT16 PolicySize; + + PolicySize = sizeof (ResultPolicy); + + // Since the policy does not yet exist, it should fail. + Status = mPolicyPpi->GetPolicy (PolicyGuid, &Attributes, &ResultPolicy, &PolicySize); + if (Status != EFI_NOT_FOUND) { + DEBUG ((DEBUG_ERROR, "%a: Unexpected return code: %r\n", __FUNCTION__, Status)); + ASSERT (FALSE); + return EFI_PROTOCOL_ERROR; + } + + // Creating the policy for the first time. + Status = mPolicyPpi->SetPolicy (PolicyGuid, 0, &Policy, sizeof (Policy)); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // After this the policy should be retrievable. This will be stored in the + // provided buffer. + Status = mPolicyPpi->GetPolicy (PolicyGuid, &Attributes, &ResultPolicy, &PolicySize); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // The policy can now be removed. + Status = mPolicyPpi->RemovePolicy (PolicyGuid); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // Trying to get the policy now should fail. + Status = mPolicyPpi->GetPolicy (PolicyGuid, &Attributes, &ResultPolicy, &PolicySize); + if (Status != EFI_NOT_FOUND) { + DEBUG ((DEBUG_ERROR, "%a: Unexpected return code: %r\n", __FUNCTION__, Status)); + ASSERT (FALSE); + return EFI_PROTOCOL_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Creates a policy that will be available to the rest of PEI and DXE. + + @param[in,out] PolicyGuid The GUID to use for the sample policy. + + @retval EFI_SUCCESS Successfully created policy. + @retval other Failure status returned by policy interface. +**/ +EFI_STATUS +PeiSampleCreatePolicy ( + IN EFI_GUID *PolicyGuid + ) +{ + EFI_STATUS Status; + SAMPLE_POLICY Policy; + + // Set sample values to be checked later. + Policy.Signature = SAMPLE_POLICY_SIGNATURE; + Policy.Revision = SAMPLE_POLICY_REVISION; + Policy.Value = SAMPLE_POLICY_VALUE; + + // Creating the policy for the first time. + Status = mPolicyPpi->SetPolicy (PolicyGuid, 0, &Policy, sizeof (Policy)); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + Entry point for the policy PEIM. Installs the policy service PPI. + + @param[in] FileHandle UNUSED. + @param[in] PeiServices UNUSED. + + @retval EFI_SUCCESS Successfully ran sample policy code. + @retval other Failure status returned by policy interface. +**/ +EFI_STATUS +EFIAPI +PeiPolicySampleEntry ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + // Retrieve the Policy PPI. + Status = PeiServicesLocatePpi ( + &gPeiPolicyPpiGuid, + 0, + NULL, + (VOID **)&mPolicyPpi + ); + + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // Run a simple set, get, and remove sample. + Status = PeiSampleSetGetRemovePolicy (&mSamplePolicyGuid1); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // Now create a policy that will be kept available for DXE. + Status = PeiSampleCreatePolicy (&mSampleGuidPeiToDxe); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + return Status; +} diff --git a/PolicyServicePkg/Samples/PolicyInterface/PolicySamplePei.inf b/PolicyServicePkg/Samples/PolicyInterface/PolicySamplePei.inf new file mode 100644 index 00000000000..96dd24929aa --- /dev/null +++ b/PolicyServicePkg/Samples/PolicyInterface/PolicySamplePei.inf @@ -0,0 +1,37 @@ +## @file +# +# This is a sample PEIM for using the policy service. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = PolicySamplePei + FILE_GUID = 8544E9C8-8D76-433F-9F2B-B2D511110769 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = PeiPolicySampleEntry + +[Sources] + PolicySamplePei.c + SamplePolicy.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + PeimEntryPoint + DebugLib + PeiServicesLib + BaseLib + +[Ppis] + gPeiPolicyPpiGuid ## CONSUMES + +[Depex] + gPeiPolicyPpiGuid diff --git a/PolicyServicePkg/Samples/PolicyInterface/README.md b/PolicyServicePkg/Samples/PolicyInterface/README.md new file mode 100644 index 00000000000..62b9ce3177b --- /dev/null +++ b/PolicyServicePkg/Samples/PolicyInterface/README.md @@ -0,0 +1,11 @@ + +# Policy Sample + +This directory contains sample PEI and DXE modules to demonstrate a basic use of +the policy service. The [PEI module](PolicySamplePei.c) demonstrates all the +interfaces available, and creates a policy to be made available to DXE. The +[DXE module](PolicySampleDxe.c) demonstrates retrieving this policy passed from +PEI to DXE. + +In this example the policy is a simple C struct, but a policy may be in whatever +data format the provider/suppliers wish. diff --git a/PolicyServicePkg/Samples/PolicyInterface/SamplePolicy.h b/PolicyServicePkg/Samples/PolicyInterface/SamplePolicy.h new file mode 100644 index 00000000000..523dc2513a3 --- /dev/null +++ b/PolicyServicePkg/Samples/PolicyInterface/SamplePolicy.h @@ -0,0 +1,33 @@ +/** @file + Defines a sample policy as a C struct + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SAMPLE_POLICY_H_ +#define _SAMPLE_POLICY_H_ + +// Sample GUID for a policy passed from PEI to DXE. In production code, this +// should be defined in the .dec file. +#define POLICY_SAMPLE_PEI_TO_DXE_GUID \ + { 0x64e1437f, 0x58f8, 0x4392, { 0xb8, 0xe3, 0x12, 0x02, 0xe4, 0x66, 0x01, 0xc8 } } + +// Example of a basic policy C struct. Policies may take any binary format, but +// A C struct is used for this example for simplicity. +typedef struct _SAMPLE_POLICY { + UINT32 Signature; + + UINT32 Revision; + + UINT32 Value; + + // Etc... +} SAMPLE_POLICY; + +#define SAMPLE_POLICY_SIGNATURE SIGNATURE_32 ('S','P','O','L') +#define SAMPLE_POLICY_REVISION (10) +#define SAMPLE_POLICY_VALUE (12345) + +#endif diff --git a/PolicyServicePkg/Test/PolicyServicePkgHostTest.dsc b/PolicyServicePkg/Test/PolicyServicePkgHostTest.dsc new file mode 100644 index 00000000000..05e6a9d4ede --- /dev/null +++ b/PolicyServicePkg/Test/PolicyServicePkgHostTest.dsc @@ -0,0 +1,27 @@ +## @file +# PolicyServicePkg DSC file used to build host-based unit tests. +# +# Copyright (c) Microsoft Corporation +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + PLATFORM_NAME = PolicyServicePkgHostTest + PLATFORM_GUID = 94003CBE-B829-4A54-90C5-A622EE550181 + PLATFORM_VERSION = 0.1 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/PolicyServicePkg/HostTest + SUPPORTED_ARCHITECTURES = IA32|X64|AARCH64 + BUILD_TARGETS = NOOPT + SKUID_IDENTIFIER = DEFAULT + +!include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc + +[LibraryClasses] + HobLib|MdeModulePkg/Library/BaseHobLibNull/BaseHobLibNull.inf + +[Components] + PolicyServicePkg/PolicyService/DxeMm/UnitTest/DxeMmPolicyUnitTest.inf + PolicyServicePkg/PolicyService/Pei/UnitTest/PeiPolicyUnitTest.inf + diff --git a/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyLibTestCommon.c b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyLibTestCommon.c new file mode 100644 index 00000000000..ff37a81b061 --- /dev/null +++ b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyLibTestCommon.c @@ -0,0 +1,210 @@ +/** @file + Implements unit tests for the policy lib. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "PolicyTest.h" + +/** + Tests scenarios when the major version is mismatched. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +MajorVersionChangeTest ( + IN UNIT_TEST_CONTEXT Context + ) + +{ + EFI_STATUS Status; + EFI_HANDLE DataHandle; + CONST EFI_GUID PolicyGuid = { + 0x7673d164, 0xe0a5, 0x4c9d, { 0xb1, 0x1f, 0xe0, 0xd7, 0x9d, 0xdd, 0xfc, 0xdc } + }; + + VERIFIED_POLICY_DESCRIPTOR TestDescriptor = { + SIGNATURE_64 ('T', 'E', 'S', 'T', 'P', 'O', 'L', '2'), + 2, + 0, + sizeof (UINT64) + }; + + // + // Create and set the policy + // + + Status = CreateVerifiedPolicy (&TestDescriptor, &DataHandle); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = SetVerifiedPolicy (&PolicyGuid, 0, DataHandle); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = CloseVerifiedPolicy (DataHandle); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + DataHandle = NULL; + + // + // Retrieve the policy. + // + + TestDescriptor.MajorVersion = 1; + Status = GetVerifiedPolicy (&PolicyGuid, &TestDescriptor, NULL, &DataHandle); + UT_ASSERT_STATUS_EQUAL (Status, EFI_INCOMPATIBLE_VERSION); + + return UNIT_TEST_PASSED; +} + +/** + Tests the basic verified test scenario. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +BasicVerifiedPolicyTest ( + IN UNIT_TEST_CONTEXT Context + ) + +{ + EFI_STATUS Status; + EFI_HANDLE DataHandle; + CONST EFI_GUID PolicyGuid = { + 0x3bc1b571, 0x4755, 0x4d6a, { 0x9d, 0x5d, 0xcb, 0x71, 0x9a, 0x9f, 0xef, 0x6a } + }; + + VERIFIED_POLICY_DESCRIPTOR TestDescriptor = { + SIGNATURE_64 ('T', 'E', 'S', 'T', 'P', 'O', 'L', '1'), + 1, + 0, + sizeof (UINT64) + }; + + DataHandle = NULL; + + // + // Create and set the policy + // + + Status = CreateVerifiedPolicy (&TestDescriptor, &DataHandle); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = SetVerifiedPolicy (&PolicyGuid, 0, DataHandle); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = CloseVerifiedPolicy (DataHandle); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + DataHandle = NULL; + + // + // Retrieve the policy. + // + + Status = GetVerifiedPolicy (&PolicyGuid, &TestDescriptor, NULL, &DataHandle); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = CloseVerifiedPolicy (DataHandle); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + return UNIT_TEST_PASSED; +} + +/** + Tests the basic test scenario. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +BasicPolicyTest ( + IN UNIT_TEST_CONTEXT Context + ) + +{ + EFI_STATUS Status; + UINT8 TestData[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + UINT8 ReadData[16] = { 0 }; + UINT16 PolicySize; + CONST EFI_GUID PolicyGuid = { + 0x30c24026, 0xee8f, 0x494a, { 0x92, 0x48, 0xc0, 0x05, 0x28, 0xc6, 0x7f, 0x5b } + }; + + PolicySize = sizeof (ReadData); + Status = GetPolicy (&PolicyGuid, NULL, &ReadData[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND); + + Status = SetPolicy (&PolicyGuid, 0, &TestData[0], sizeof (TestData)); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = GetPolicy (&PolicyGuid, NULL, &ReadData[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_MEM_EQUAL (&TestData[0], &ReadData[0], sizeof (TestData)); + + Status = RemovePolicy (&PolicyGuid); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + Status = RemovePolicy (&PolicyGuid); + UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND); + + return UNIT_TEST_PASSED; +} + +/** + Add the common policy library tests. + + @param[in] Framework The test framework to add the common policy + library tests + + @retval EFI_SUCCESS Test added to framework. + @retval OTHER Error returned by subfunction. +**/ +EFI_STATUS +PolicyLibCommonCreateTests ( + UNIT_TEST_FRAMEWORK_HANDLE Framework + ) + +{ + UNIT_TEST_SUITE_HANDLE LibCommonTests; + EFI_STATUS Status; + + Status = CreateUnitTestSuite ( + &LibCommonTests, + Framework, + "Common Policy Library Tests", + "Policy.Lib.Common", + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + AddTestCase (LibCommonTests, "Tests the basic policy lib interfaces", "BasicPolicyTest", BasicPolicyTest, NULL, NULL, NULL); + AddTestCase (LibCommonTests, "Test basic verified policy creation", "BasicVerifiedPolicyTest", BasicVerifiedPolicyTest, NULL, NULL, NULL); + AddTestCase (LibCommonTests, "Test change of major version", "MajorVersionChangeTest", MajorVersionChangeTest, NULL, NULL, NULL); + + return EFI_SUCCESS; +} diff --git a/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyServiceTestCommon.c b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyServiceTestCommon.c new file mode 100644 index 00000000000..c6965ff8660 --- /dev/null +++ b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyServiceTestCommon.c @@ -0,0 +1,267 @@ +/** @file + Implements unit tests for the policy service. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "PolicyTest.h" + +/** + Tests the basic lifecycle of a policy. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +BasicCreatePolicyTest ( + IN UNIT_TEST_CONTEXT Context + ) + +{ + EFI_STATUS Status; + UINT8 SetPolicy[100]; + UINT8 GetPolicy[100]; + UINT16 PolicySize; + CONST EFI_GUID PolicyGuid = { + 0x576e3fbc, 0x0cf1, 0x4b54, { 0x82, 0xc1, 0x98, 0xbf, 0xff, 0xa0, 0xcb, 0x7e } + }; + + PolicySize = 0; + SetMem (&SetPolicy[0], sizeof (SetPolicy), 0xCD); + ZeroMem (&GetPolicy[0], sizeof (GetPolicy)); + + Status = mPolicyInterface->GetPolicy (&PolicyGuid, NULL, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND); + + Status = mPolicyInterface->SetPolicy (&PolicyGuid, 0, &SetPolicy[0], sizeof (SetPolicy)); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + // + // Ensure the get policy returns the correct size. + // + + PolicySize = 0; + + Status = mPolicyInterface->GetPolicy (&PolicyGuid, NULL, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_BUFFER_TOO_SMALL); + UT_ASSERT_EQUAL (PolicySize, sizeof (SetPolicy)); + + // + // Check the policy comes back correctly. + // + + Status = mPolicyInterface->GetPolicy (&PolicyGuid, NULL, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (PolicySize, sizeof (SetPolicy)); + UT_ASSERT_MEM_EQUAL (&GetPolicy[0], &SetPolicy[0], PolicySize); + + // + // Check the policy can be removed. + // + + Status = mPolicyInterface->RemovePolicy (&PolicyGuid); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = mPolicyInterface->GetPolicy (&PolicyGuid, NULL, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND); + + return UNIT_TEST_PASSED; +} + +/** + Tests scenarios where multiple policies are created with the same GUID. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +DuplicatePolicyTest ( + IN UNIT_TEST_CONTEXT Context + ) + +{ + EFI_STATUS Status; + UINT8 SetPolicy[100]; + UINT8 GetPolicy[100]; + UINT16 PolicySize; + UINT64 Attributes; + CONST EFI_GUID PolicyGuid = { + 0x9af7da34, 0x0f81, 0x4921, { 0xa1, 0x98, 0xa9, 0x04, 0x1a, 0x33, 0xa1, 0x9f } + }; + + PolicySize = 0; + ZeroMem (&GetPolicy[0], sizeof (GetPolicy)); + ZeroMem (&SetPolicy[0], sizeof (SetPolicy)); + + Status = mPolicyInterface->SetPolicy (&PolicyGuid, 0, &SetPolicy[0], sizeof (SetPolicy)); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + // + // Override the policy with a different value. + // + + SetMem (&SetPolicy[0], sizeof (SetPolicy), 0xCD); + Status = mPolicyInterface->SetPolicy (&PolicyGuid, POLICY_ATTRIBUTE_FINALIZED, &SetPolicy[0], sizeof (SetPolicy)); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + PolicySize = sizeof (SetPolicy); + Status = mPolicyInterface->GetPolicy (&PolicyGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (PolicySize, sizeof (SetPolicy)); + UT_ASSERT_EQUAL (Attributes, POLICY_ATTRIBUTE_FINALIZED); + UT_ASSERT_MEM_EQUAL (&GetPolicy[0], &SetPolicy[0], PolicySize); + + // + // Ensure the policy cannot be written after being finalized. + // + + SetMem (&SetPolicy[0], sizeof (SetPolicy), 0xCD); + Status = mPolicyInterface->SetPolicy (&PolicyGuid, POLICY_ATTRIBUTE_FINALIZED, &SetPolicy[0], sizeof (SetPolicy)); + UT_ASSERT_STATUS_EQUAL (Status, EFI_ACCESS_DENIED); + + Status = mPolicyInterface->RemovePolicy (&PolicyGuid); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + return UNIT_TEST_PASSED; +} + +/** + Tests multiple policies existing at once. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +MultiplePolicyTest ( + IN UNIT_TEST_CONTEXT Context + ) + +{ + EFI_STATUS Status; + UINT64 Policies[10]; + UINT64 GetPolicy; + UINT32 PolicyIndex; + UINT16 PolicySize; + CONST EFI_GUID PolicyGuids[10] = { + { 0xaff48896, 0x6725, 0x40a8, { 0xa1, 0x8e, 0x69, 0xb8, 0x36, 0x88, 0xfa, 0x71 } + }, + { 0xd3f329d8, 0x5ab5, 0x471c, { 0xaf, 0x32, 0x1f, 0x94, 0xde, 0xba, 0x71, 0x02 } + }, + { 0x0b490c23, 0x09a5, 0x4f5c, { 0x97, 0xee, 0xc0, 0x83, 0xaf, 0xbb, 0x21, 0x76 } + }, + { 0xcf81f8c6, 0xc65f, 0x44d3, { 0x92, 0xaa, 0x36, 0xf6, 0x0f, 0xd8, 0x9f, 0x20 } + }, + { 0x67f9e373, 0x99a2, 0x43d6, { 0x83, 0x7f, 0xc4, 0xe1, 0xba, 0x6d, 0x0b, 0xc1 } + }, + { 0x0b3cfd24, 0xcfe3, 0x4614, { 0x81, 0x78, 0xc1, 0x4a, 0x12, 0xe2, 0x15, 0x5e } + }, + { 0xc6c8edfe, 0x7867, 0x4aac, { 0xa8, 0x02, 0xda, 0xde, 0xe3, 0x98, 0x1e, 0x50 } + }, + { 0xa7f9df09, 0x5502, 0x45cb, { 0x93, 0x77, 0xd4, 0x80, 0x09, 0x30, 0xed, 0xe8 } + }, + { 0x1daddb05, 0xd36f, 0x4e6c, { 0x8b, 0x6b, 0x64, 0x7b, 0xb9, 0x2b, 0x29, 0x65 } + }, + { 0x46433b36, 0x871a, 0x4755, { 0x96, 0x36, 0xce, 0x9f, 0x3a, 0x44, 0x67, 0x51 } + } + }; + + // + // Set all of the policies. + // + + for (PolicyIndex = 0; PolicyIndex < 10; PolicyIndex++) { + Policies[PolicyIndex] = PolicyIndex; + PolicySize = 0; + + Status = mPolicyInterface->GetPolicy (&PolicyGuids[PolicyIndex], NULL, &GetPolicy, &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND); + + Status = mPolicyInterface->SetPolicy (&PolicyGuids[PolicyIndex], 0, &Policies[PolicyIndex], sizeof (Policies[0])); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + } + + // + // Get all the policies. + // + + for (PolicyIndex = 0; PolicyIndex < 10; PolicyIndex++) { + PolicySize = sizeof (Policies[0]); + + Status = mPolicyInterface->GetPolicy (&PolicyGuids[PolicyIndex], NULL, &GetPolicy, &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (PolicySize, sizeof (Policies[0])); + UT_ASSERT_EQUAL (GetPolicy, Policies[PolicyIndex]); + } + + // + // Remove all the policies. + // + + for (PolicyIndex = 0; PolicyIndex < 10; PolicyIndex++) { + PolicySize = sizeof (Policies[0]); + + Status = mPolicyInterface->RemovePolicy (&PolicyGuids[PolicyIndex]); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + } + + return UNIT_TEST_PASSED; +} + +/** + Add the common policy service tests. + + @param[in] Framework The test framework to add the common policy + service tests + + @retval EFI_SUCCESS Test added to framework. + @retval OTHER Error returned by subfunction. +**/ +EFI_STATUS +PolicyServiceCommonCreateTests ( + UNIT_TEST_FRAMEWORK_HANDLE Framework + ) + +{ + UNIT_TEST_SUITE_HANDLE ServiceCommonTests; + EFI_STATUS Status; + + Status = CreateUnitTestSuite ( + &ServiceCommonTests, + Framework, + "Common Policy Service Tests", + "Policy.Service.Common", + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + AddTestCase (ServiceCommonTests, "Test basic policy creation", "BasicCreatePolicyTest", BasicCreatePolicyTest, NULL, NULL, NULL); + AddTestCase (ServiceCommonTests, "Test duplicate/override policy", "DuplicatePolicyTest", DuplicatePolicyTest, NULL, NULL, NULL); + AddTestCase (ServiceCommonTests, "Multiple policy test", "MultiplePolicyTest", MultiplePolicyTest, NULL, NULL, NULL); + + return EFI_SUCCESS; +} diff --git a/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTest.h b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTest.h new file mode 100644 index 00000000000..3170b13ef8c --- /dev/null +++ b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTest.h @@ -0,0 +1,47 @@ +/** @file + Definitions for the policy unit tests. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef POLICY_TEST_H_ +#define POLICY_TEST_H_ + +extern POLICY_INTERFACE *mPolicyInterface; + +// +// Test GUIDs +// + +#define PEI_TO_DXE_TEST_GUID { 0x90c07ec2, 0x731a, 0x48a7, { 0x80, 0x0a, 0xa2, 0x81, 0x17, 0x1d, 0xa4, 0xdb } } +#define PEI_TO_DXE_TEST_GUID_FINALIZED { 0x50c7e443, 0x7060, 0x46f9, { 0xbf, 0xdf, 0xa7, 0x00, 0x6b, 0x29, 0xa8, 0x8c } } + +// +// Test structures. +// + +#define PEI_TO_DXE_POLICY_SIZE (10) +#define PEI_TO_DXE_POLICY {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09} +#define PEI_TO_DXE_POLICY_FINALIZED {0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9} + +// +// Common service tests. +// + +EFI_STATUS +PolicyServiceCommonCreateTests ( + UNIT_TEST_FRAMEWORK_HANDLE Framework + ); + +// +// Common lib tests. +// + +EFI_STATUS +PolicyLibCommonCreateTests ( + UNIT_TEST_FRAMEWORK_HANDLE Framework + ); + +#endif diff --git a/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestDxe.c b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestDxe.c new file mode 100644 index 00000000000..e9543d158ae --- /dev/null +++ b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestDxe.c @@ -0,0 +1,226 @@ +/** @file + Implements sample policy for DXE environment. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "PolicyTest.h" + +#define UNIT_TEST_APP_NAME "Policy DXE Unit Tests" +#define UNIT_TEST_APP_VERSION "1.0" + +// +// Global to store the protocol. +// +POLICY_PROTOCOL *mPolicyProtocol; +POLICY_INTERFACE *mPolicyInterface; + +/** + Test DXE specific scenarios. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +IngestedPolicyTest ( + IN UNIT_TEST_CONTEXT Context + ) + +{ + CONST EFI_GUID PolicyGuid = PEI_TO_DXE_TEST_GUID; + CONST EFI_GUID PolicyFinalGuid = PEI_TO_DXE_TEST_GUID_FINALIZED; + UINT8 Policy[PEI_TO_DXE_POLICY_SIZE] = PEI_TO_DXE_POLICY; + UINT8 PolicyFinal[PEI_TO_DXE_POLICY_SIZE] = PEI_TO_DXE_POLICY_FINALIZED; + UINT8 GetPolicy[PEI_TO_DXE_POLICY_SIZE]; + EFI_STATUS Status; + UINT16 PolicySize; + UINT64 Attributes; + + ZeroMem (&GetPolicy[0], PEI_TO_DXE_POLICY_SIZE); + PolicySize = 0; + + // + // Ensure the policy can be retrieved. + // + + Status = mPolicyProtocol->GetPolicy (&PolicyGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_BUFFER_TOO_SMALL); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), 0); + + Status = mPolicyProtocol->GetPolicy (&PolicyGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), 0); + UT_ASSERT_MEM_EQUAL (&GetPolicy[0], &Policy[0], PolicySize); + + // + // The policy should be updatable without problem. + // + + Policy[0] = 0xAF; + Policy[5] = 0xFA; + Status = mPolicyProtocol->SetPolicy (&PolicyGuid, POLICY_ATTRIBUTE_FINALIZED, &Policy[0], PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = mPolicyProtocol->GetPolicy (&PolicyGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), POLICY_ATTRIBUTE_FINALIZED); + UT_ASSERT_MEM_EQUAL (&GetPolicy[0], &Policy[0], PolicySize); + + // + // The policy should also be removeable without any trace of the previous values. + // + + Status = mPolicyProtocol->RemovePolicy (&PolicyGuid); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = mPolicyProtocol->GetPolicy (&PolicyGuid, NULL, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND); + + // + // Check the finalized policy also exists. + // + + PolicySize = 0; + + Status = mPolicyProtocol->GetPolicy (&PolicyFinalGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_BUFFER_TOO_SMALL); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), POLICY_ATTRIBUTE_FINALIZED); + + Status = mPolicyProtocol->GetPolicy (&PolicyFinalGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), POLICY_ATTRIBUTE_FINALIZED); + UT_ASSERT_MEM_EQUAL (&GetPolicy[0], &PolicyFinal[0], PolicySize); + + // + // The policy should not be updatable. + // + + PolicyFinal[0] = 0xAB; + PolicyFinal[5] = 0xBA; + Status = mPolicyProtocol->SetPolicy (&PolicyFinalGuid, 0, &PolicyFinal[0], PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_STATUS_EQUAL (Status, EFI_ACCESS_DENIED); + + // + // Remove the policy. + // + + Status = mPolicyProtocol->RemovePolicy (&PolicyFinalGuid); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = mPolicyProtocol->GetPolicy (&PolicyFinalGuid, NULL, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND); + + return UNIT_TEST_PASSED; +} + +/** + Test driver entry point. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable UNUSED. + + @retval EFI_SUCCESS Policy store initialized and protocol installed. + @retval other Sample routines returned a failure. +**/ +EFI_STATUS +EFIAPI +DxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE ServiceDxeTests; + + DEBUG ( + (DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION) + ); + + Status = gBS->LocateProtocol ( + &gPolicyProtocolGuid, + NULL, + (VOID **)&mPolicyProtocol + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to locate policy protocol. Status = %r\n", Status)); + goto EXIT; + } + + mPolicyInterface = mPolicyProtocol; + + Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + // + // Add the common service tests. + // + + Status = PolicyServiceCommonCreateTests (Framework); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in PolicyServiceCommonCreateTests. Status = %r\n", Status)); + goto EXIT; + } + + // + // Add the common lib tests. + // + + Status = PolicyLibCommonCreateTests (Framework); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in PolicyLibCommonCreateTests. Status = %r\n", Status)); + goto EXIT; + } + + // + // Add the DXE service tests. + // + + Status = CreateUnitTestSuite ( + &ServiceDxeTests, + Framework, + "DXE Policy Service Tests", + "Policy.Service.Dxe", + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to create DXE service tests. Status = %r\n", Status)); + goto EXIT; + } + + AddTestCase (ServiceDxeTests, "Test PEI created policies", "IngestedPolicyTest", IngestedPolicyTest, NULL, NULL, NULL); + + // + // Execute the tests. + // + + Status = RunAllTestSuites (Framework); + +EXIT: + return Status; +} diff --git a/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestDxe.inf b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestDxe.inf new file mode 100644 index 00000000000..ab6d724fc00 --- /dev/null +++ b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestDxe.inf @@ -0,0 +1,42 @@ +## @file +# +# This is a sample DXE driver for using the policy service. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = PolicyTestDxe + FILE_GUID = F00D0B09-7D28-4DBF-B79B-998C61FCACD9 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DxeEntryPoint + +[Sources] + PolicyTestDxe.c + PolicyServiceTestCommon.c + PolicyLibTestCommon.c + PolicyTest.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + UefiRuntimeServicesTableLib + DebugLib + UnitTestLib + PrintLib + PolicyLib + +[Protocols] + gPolicyProtocolGuid ## CONSUMES + +[Depex] + gPolicyProtocolGuid diff --git a/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestMm.c b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestMm.c new file mode 100644 index 00000000000..5e4c285e32a --- /dev/null +++ b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestMm.c @@ -0,0 +1,237 @@ +/** @file + Implements sample policy for MM environment. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "PolicyTest.h" + +#define UNIT_TEST_APP_NAME "Policy MM Unit Tests" +#define UNIT_TEST_APP_VERSION "1.0" + +// +// Global to store the protocol. +// +MM_POLICY_PROTOCOL *mPolicyProtocol; +POLICY_INTERFACE *mPolicyInterface; + +/** + Test MM specific scenarios. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +IngestedPolicyTest ( + IN UNIT_TEST_CONTEXT Context + ) + +{ + CONST EFI_GUID PolicyGuid = PEI_TO_DXE_TEST_GUID; + CONST EFI_GUID PolicyFinalGuid = PEI_TO_DXE_TEST_GUID_FINALIZED; + UINT8 Policy[PEI_TO_DXE_POLICY_SIZE] = PEI_TO_DXE_POLICY; + UINT8 PolicyFinal[PEI_TO_DXE_POLICY_SIZE] = PEI_TO_DXE_POLICY_FINALIZED; + UINT8 GetPolicy[PEI_TO_DXE_POLICY_SIZE]; + EFI_STATUS Status; + UINT16 PolicySize; + UINT64 Attributes; + + // + // Note: While this code runs in MM it re-uses the policies intended for DXE + // for simplicity since they will be available in both cases. + // + + ZeroMem (&GetPolicy[0], PEI_TO_DXE_POLICY_SIZE); + PolicySize = 0; + + // + // Ensure the policy can be retrieved. + // + + Status = mPolicyProtocol->GetPolicy (&PolicyGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_BUFFER_TOO_SMALL); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), 0); + + Status = mPolicyProtocol->GetPolicy (&PolicyGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), 0); + UT_ASSERT_MEM_EQUAL (&GetPolicy[0], &Policy[0], PolicySize); + + // + // The policy should be updatable without problem. + // + + Policy[0] = 0xAF; + Policy[5] = 0xFA; + Status = mPolicyProtocol->SetPolicy (&PolicyGuid, POLICY_ATTRIBUTE_FINALIZED, &Policy[0], PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = mPolicyProtocol->GetPolicy (&PolicyGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), POLICY_ATTRIBUTE_FINALIZED); + UT_ASSERT_MEM_EQUAL (&GetPolicy[0], &Policy[0], PolicySize); + + // + // The policy should also be removeable without any trace of the previous values. + // + + Status = mPolicyProtocol->RemovePolicy (&PolicyGuid); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = mPolicyProtocol->GetPolicy (&PolicyGuid, NULL, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND); + + // + // Check the finalized policy also exists. + // + + PolicySize = 0; + + Status = mPolicyProtocol->GetPolicy (&PolicyFinalGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_BUFFER_TOO_SMALL); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), POLICY_ATTRIBUTE_FINALIZED); + + Status = mPolicyProtocol->GetPolicy (&PolicyFinalGuid, &Attributes, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (PolicySize, PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_EQUAL ((Attributes & POLICY_ATTRIBUTE_FINALIZED), POLICY_ATTRIBUTE_FINALIZED); + UT_ASSERT_MEM_EQUAL (&GetPolicy[0], &PolicyFinal[0], PolicySize); + + // + // The policy should not be updatable. + // + + PolicyFinal[0] = 0xAB; + PolicyFinal[5] = 0xBA; + Status = mPolicyProtocol->SetPolicy (&PolicyFinalGuid, 0, &PolicyFinal[0], PEI_TO_DXE_POLICY_SIZE); + UT_ASSERT_STATUS_EQUAL (Status, EFI_ACCESS_DENIED); + + // + // Remove the policy. + // + + Status = mPolicyProtocol->RemovePolicy (&PolicyFinalGuid); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = mPolicyProtocol->GetPolicy (&PolicyFinalGuid, NULL, &GetPolicy[0], &PolicySize); + UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND); + + return UNIT_TEST_PASSED; +} + +/** + Test driver entry point. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable UNUSED. + + @retval EFI_SUCCESS Policy store initialized and protocol installed. + @retval other Sample routines returned a failure. +**/ +EFI_STATUS +EFIAPI +MmEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE ServiceDxeTests; + + DEBUG ( + (DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION) + ); + + Status = gMmst->MmLocateProtocol ( + &gMmPolicyProtocolGuid, + NULL, + (VOID **)&mPolicyProtocol + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to locate policy protocol. Status = %r\n", Status)); + goto EXIT; + } + + mPolicyInterface = mPolicyProtocol; + + Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + // + // Add the common service tests. + // + + Status = PolicyServiceCommonCreateTests (Framework); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in PolicyServiceCommonCreateTests. Status = %r\n", Status)); + goto EXIT; + } + + // + // Add the common lib tests. + // + + Status = PolicyLibCommonCreateTests (Framework); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in PolicyLibCommonCreateTests. Status = %r\n", Status)); + goto EXIT; + } + + // + // Add the DXE service tests. + // + + Status = CreateUnitTestSuite ( + &ServiceDxeTests, + Framework, + "MM Policy Service Tests", + "Policy.Service.Mm", + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to create MM service tests. Status = %r\n", Status)); + goto EXIT; + } + + // + // On AARCH64, PEI will not run before standalone so skip the ingestion test. + // + + #if !defined (MDE_CPU_AARCH64) + AddTestCase (ServiceDxeTests, "Test PEI created policies", "IngestedPolicyTest", IngestedPolicyTest, NULL, NULL, NULL); + #endif + + // + // Execute the tests. + // + + Status = RunAllTestSuites (Framework); + +EXIT: + return Status; +} diff --git a/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestMm.inf b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestMm.inf new file mode 100644 index 00000000000..43ec4cbeb54 --- /dev/null +++ b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestMm.inf @@ -0,0 +1,42 @@ +## @file +# +# This is a sample MM driver for using the policy service. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010017 + PI_SPECIFICATION_VERSION = 0x00010032 + BASE_NAME = PolicyTestMm + FILE_GUID = E190B7DB-1FCD-4B23-8310-5BF9622614EB + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + ENTRY_POINT = MmEntryPoint + +[Sources] + PolicyTestMm.c + PolicyServiceTestCommon.c + PolicyLibTestCommon.c + PolicyTest.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + StandaloneMmDriverEntryPoint + MmServicesTableLib + UnitTestLib + PolicyLib + +[Protocols] + gMmPolicyProtocolGuid ## CONSUMES + +[Depex] + TRUE diff --git a/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestPei.c b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestPei.c new file mode 100644 index 00000000000..14f9388af31 --- /dev/null +++ b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestPei.c @@ -0,0 +1,151 @@ +/** @file + Implements sample policy for DXE environment. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include +#include + +#include "PolicyTest.h" + +#define UNIT_TEST_APP_NAME "Policy PEI Unit Tests" +#define UNIT_TEST_APP_VERSION "1.0" + +// +// Global to store the protocol. +// +POLICY_PPI *mPolicyPpi; +POLICY_INTERFACE *mPolicyInterface; + +/** + Tests creating a policy to be consumed by later test phases. + + @param[in] Context Unused. + + @retval UNIT_TEST_PASSED Test passed. + @retval UNIT_TEST_ERROR_TEST_FAILED Test failed. +**/ +UNIT_TEST_STATUS +EFIAPI +PeiPersistenceTest ( + IN UNIT_TEST_CONTEXT Context + ) + +{ + CONST EFI_GUID PolicyGuid = PEI_TO_DXE_TEST_GUID; + CONST EFI_GUID PolicyFinalGuid = PEI_TO_DXE_TEST_GUID_FINALIZED; + UINT8 Policy[PEI_TO_DXE_POLICY_SIZE] = PEI_TO_DXE_POLICY; + UINT8 PolicyFinal[PEI_TO_DXE_POLICY_SIZE] = PEI_TO_DXE_POLICY_FINALIZED; + EFI_STATUS Status; + + Status = mPolicyPpi->SetPolicy (&PolicyGuid, 0, &Policy[0], sizeof (Policy)); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + Status = mPolicyPpi->SetPolicy (&PolicyFinalGuid, POLICY_ATTRIBUTE_FINALIZED, &PolicyFinal[0], sizeof (PolicyFinal)); + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + + return UNIT_TEST_PASSED; +} + +/** + Entry point for the policy PEIM. Installs the policy service PPI. + + @param[in] FileHandle UNUSED. + @param[in] PeiServices UNUSED. + + @retval EFI_SUCCESS The interface was successfully installed. + @retval EFI_OUT_OF_RESOURCES There is no additional space in the PPI database. +**/ +EFI_STATUS +EFIAPI +PeiEntryPoint ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE ServicePeiTests; + + DEBUG ( + (DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION) + ); + + // Retrieve the Policy PPI. + Status = PeiServicesLocatePpi ( + &gPeiPolicyPpiGuid, + 0, + NULL, + (VOID **)&mPolicyPpi + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to locate policy PPI. Status = %r\n", Status)); + goto EXIT; + } + + mPolicyInterface = mPolicyPpi; + + Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + // + // Add the common service tests. + // + + Status = PolicyServiceCommonCreateTests (Framework); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in PolicyServiceCommonCreateTests. Status = %r\n", Status)); + goto EXIT; + } + + // + // Add the common lib tests. + // + + Status = PolicyLibCommonCreateTests (Framework); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in PolicyLibCommonCreateTests. Status = %r\n", Status)); + goto EXIT; + } + + // + // Add the PEI service tests. + // + + Status = CreateUnitTestSuite ( + &ServicePeiTests, + Framework, + "PEI Policy Service Tests", + "Policy.Service.Pei", + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to create PEI service tests. Status = %r\n", Status)); + goto EXIT; + } + + AddTestCase (ServicePeiTests, "Create PEI policies for DXE", "PeiPersistenceTest", PeiPersistenceTest, NULL, NULL, NULL); + + // + // Execute the tests. + // + + Status = RunAllTestSuites (Framework); + +EXIT: + return Status; +} diff --git a/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestPei.inf b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestPei.inf new file mode 100644 index 00000000000..6477f58eea6 --- /dev/null +++ b/PolicyServicePkg/Test/UnitTest/PolicyTest/PolicyTestPei.inf @@ -0,0 +1,41 @@ +## @file +# +# This is a sample DXE driver for using the policy service. +# +# Copyright (C) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = PolicyTestPei + FILE_GUID = 28B53789-51B6-4B47-970C-10205C3DF660 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = PeiEntryPoint + +[Sources] + PolicyTestPei.c + PolicyServiceTestCommon.c + PolicyLibTestCommon.c + PolicyTest.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + PolicyServicePkg/PolicyServicePkg.dec + +[LibraryClasses] + PeimEntryPoint + BaseLib + DebugLib + UnitTestLib + PrintLib + PolicyLib + +[Ppis] + gPeiPolicyPpiGuid ## CONSUMES + +[Depex] + gPeiPolicyPpiGuid AND gEfiPeiMemoryDiscoveredPpiGuid