diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 5b1f50e9c0..1aedfe280a 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -400,6 +400,7 @@ MdeModulePkg/Application/VariableInfo/VariableInfo.inf MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf MdeModulePkg/Universal/Variable/Pei/VariablePei.inf + MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.inf MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/TimestampDxe/TimestampDxe.inf MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf diff --git a/MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.c b/MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.c new file mode 100644 index 0000000000..4f937e22e8 --- /dev/null +++ b/MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.c @@ -0,0 +1,381 @@ +/** @file -- MmVariablePei.c + Provides interface for reading Secure System Variables during PEI. + + Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "MmVariablePei.h" + +#define MM_VARIABLE_COMM_BUFFER_OFFSET (SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) + +// +// Module globals +// +EFI_PEI_READ_ONLY_VARIABLE2_PPI mPeiSecureVariableRead = { + PeiMmGetVariable, + PeiMmGetNextVariableName +}; + +EFI_PEI_PPI_DESCRIPTOR mPeiMmVariablePpi = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiReadOnlyVariable2PpiGuid, + &mPeiSecureVariableRead +}; + +/** + Entry point of PEI Secure Variable read driver + + @param FileHandle Handle of the file being invoked. + Type EFI_PEI_FILE_HANDLE is defined in FfsFindNextFile(). + @param PeiServices General purpose services available to every PEIM. + + @retval EFI_SUCCESS If the interface could be successfully installed + @retval Others Returned from PeiServicesInstallPpi() +**/ +EFI_STATUS +EFIAPI +PeiMmVariableInitialize ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + return PeiServicesInstallPpi (&mPeiMmVariablePpi); +} + +/** + Helper function to populate MM communicate header and variable communicate header + and then communicate to PEI. + + @param[in, out] CommunicateBuffer Size of the variable name. + @param[in] CommunicateBufferSize The entire buffer size to be sent to MM. + @param[in] Function The MM variable function value. + + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_SUCCESS Find the specified variable. + @retval Others Errors returned by MM communicate or variable service. + +**/ +EFI_STATUS +PopulateHeaderAndCommunicate ( + IN OUT UINT8 *CommunicateBuffer, + IN UINTN CommunicateBufferSize, + IN UINTN Function + ) +{ + EFI_STATUS Status; + EFI_PEI_MM_COMMUNICATION_PPI *MmCommunicationPpi; + EFI_MM_COMMUNICATE_HEADER *MmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *MmVarCommsHeader; + + // Minimal sanity check + if ((CommunicateBuffer == NULL) || + (CommunicateBufferSize < MM_VARIABLE_COMM_BUFFER_OFFSET)) + { + Status = EFI_INVALID_PARAMETER; + DEBUG ((DEBUG_ERROR, "%a: Invalid incoming parameters: %p and 0x%x\n", __func__, CommunicateBuffer, CommunicateBufferSize)); + goto Exit; + } + + if ((Function != SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME) && + (Function != SMM_VARIABLE_FUNCTION_GET_VARIABLE)) + { + Status = EFI_INVALID_PARAMETER; + DEBUG ((DEBUG_ERROR, "%a: Invalid function value: 0x%x\n", __func__, Function)); + goto Exit; + } + + Status = PeiServicesLocatePpi (&gEfiPeiMmCommunicationPpiGuid, 0, NULL, (VOID **)&MmCommunicationPpi); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to locate PEI MM Communication PPI: %r\n", __func__, Status)); + goto Exit; + } + + // Zero the entire Communication Buffer Header + MmCommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *)CommunicateBuffer; + + ZeroMem (MmCommunicateHeader, SMM_COMMUNICATE_HEADER_SIZE); + + // Use gEfiSmmVariableProtocolGuid to request the MM variable service in Standalone MM + CopyMem ((VOID *)&MmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid, sizeof (GUID)); + + // Program the MM header size + MmCommunicateHeader->MessageLength = CommunicateBufferSize - SMM_COMMUNICATE_HEADER_SIZE; + + MmVarCommsHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)(CommunicateBuffer + SMM_COMMUNICATE_HEADER_SIZE); + + // We are only supporting GetVariable and GetNextVariableName + MmVarCommsHeader->Function = Function; + + // Send the MM request using MmCommunicationPei + Status = MmCommunicationPpi->Communicate (MmCommunicationPpi, CommunicateBuffer, &CommunicateBufferSize); + if (EFI_ERROR (Status)) { + // Received an error from MM interface. + DEBUG ((DEBUG_ERROR, "%a - MM Interface Error: %r\n", __func__, Status)); + goto Exit; + } + + // MM request was successfully handled by the framework. + // Set status to the Variable Service Status Code + Status = MmVarCommsHeader->ReturnStatus; + if (EFI_ERROR (Status)) { + // We received an error from Variable Service. + // We cant do anymore so return Status + if (Status != EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_ERROR, "%a - Variable Service Error: %r\n", __func__, Status)); + } + + goto Exit; + } + +Exit: + return Status; +} + +/** + This service retrieves a variable's value using its name and GUID. + + This function is using the Secure Variable Store. If the Data + buffer is too small to hold the contents of the variable, the error + EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer + size to obtain the data. + + @param This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI. + @param VariableName A pointer to a null-terminated string that is the variable's name. + @param VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of + VariableGuid and VariableName must be unique. + @param Attributes If non-NULL, on return, points to the variable's attributes. + @param DataSize On entry, points to the size in bytes of the Data buffer. + On return, points to the size of the data returned in Data. + @param Data Points to the buffer which will hold the returned variable value. + May be NULL with a zero DataSize in order to determine the size of the buffer needed. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable was not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting data. + DataSize is updated with the size required for + the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or Data is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. + +**/ +EFI_STATUS +EFIAPI +PeiMmGetVariable ( + IN CONST EFI_PEI_READ_ONLY_VARIABLE2_PPI *This, + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VariableGuid, + OUT UINT32 *Attributes, OPTIONAL + IN OUT UINTN *DataSize, + OUT VOID *Data OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN MessageSize; + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *MmVarAccessHeader; + UINT8 *MmCommunicateBuffer; + UINTN RequiredPages; + + // Check input parameters + if ((VariableName == NULL) || (VariableGuid == NULL) || (DataSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (VariableName[0] == 0) { + return EFI_NOT_FOUND; + } + + if ((*DataSize > 0) && (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Allocate required pages to send MM request + MessageSize = MM_VARIABLE_COMM_BUFFER_OFFSET + + OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + + StrSize (VariableName) + *DataSize; + + RequiredPages = EFI_SIZE_TO_PAGES (MessageSize); + MmCommunicateBuffer = (UINT8 *)AllocatePages (RequiredPages); + + if (MmCommunicateBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG ((DEBUG_ERROR, "%a: Failed to allocate memory: %r\n", __func__, Status)); + return Status; + } + + // Zero the entire Communication Buffer + ZeroMem (MmCommunicateBuffer, (RequiredPages * EFI_PAGE_SIZE)); + + // + // Program all payload structure contents + // + MmVarAccessHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *)(MmCommunicateBuffer + MM_VARIABLE_COMM_BUFFER_OFFSET); + + // Variable GUID + CopyMem ((VOID *)&MmVarAccessHeader->Guid, VariableGuid, sizeof (GUID)); + + // Program the max amount of data we accept. + MmVarAccessHeader->DataSize = *DataSize; + + // Get size of the variable name + MmVarAccessHeader->NameSize = StrSize (VariableName); + + // Populate incoming variable name + CopyMem ((VOID *)&MmVarAccessHeader->Name, VariableName, MmVarAccessHeader->NameSize); + + Status = PopulateHeaderAndCommunicate (MmCommunicateBuffer, MessageSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE); + if (EFI_ERROR (Status)) { + // We received an error from either communicate or Variable Service. + if (Status != EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_ERROR, "%a - Communite to MM for variable service errored: %r\n", __func__, Status)); + } + + goto Exit; + } + + Status = EFI_SUCCESS; + + // User provided buffer is too small + if (*DataSize < MmVarAccessHeader->DataSize) { + Status = EFI_BUFFER_TOO_SMALL; + } + +Exit: + // Check if we need to set Attributes + if (Attributes != NULL) { + *Attributes = MmVarAccessHeader->Attributes; + } + + *DataSize = MmVarAccessHeader->DataSize; + + if (Status == EFI_SUCCESS) { + CopyMem ((VOID *)Data, (UINT8 *)MmVarAccessHeader->Name + MmVarAccessHeader->NameSize, *DataSize); + } + + // Free the Communication Buffer + if (MmCommunicateBuffer != NULL) { + FreePages (MmCommunicateBuffer, RequiredPages); + } + + return Status; +} + +/** + Return the next variable name and GUID. + + This function is called multiple times to retrieve the VariableName + and VariableGuid of all variables currently available in the system. + On each call, the previous results are passed into the interface, + and, on return, the interface returns the data for the next + interface. When the entire variable list has been returned, + EFI_NOT_FOUND is returned. + + @param This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI. + + @param VariableNameSize On entry, points to the size of the buffer pointed to by VariableName. + On return, the size of the variable name buffer. + @param VariableName On entry, a pointer to a null-terminated string that is the variable's name. + On return, points to the next variable's null-terminated name string. + + @param VariableGuid On entry, a pointer to an EFI_GUID that is the variable's GUID. + On return, a pointer to the next variable's GUID. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable could not be found. + @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the resulting + data. VariableNameSize is updated with the size + required for the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or + VariableNameSize is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. +**/ +EFI_STATUS +EFIAPI +PeiMmGetNextVariableName ( + IN CONST EFI_PEI_READ_ONLY_VARIABLE2_PPI *This, + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VariableGuid + ) +{ + EFI_STATUS Status; + UINTN MessageSize; + UINT8 *MmCommunicateBuffer; + SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *MmVarGetNextVarHeader; + UINTN RequiredPages; + + // Check input parameters + if ((VariableName == NULL) || + (VariableGuid == NULL) || + (VariableNameSize == NULL) || + (*VariableNameSize == 0)) + { + return EFI_INVALID_PARAMETER; + } + + // Allocate required pages to send MM request + MessageSize = MM_VARIABLE_COMM_BUFFER_OFFSET + + OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + + StrSize (VariableName) + *VariableNameSize; + + RequiredPages = EFI_SIZE_TO_PAGES (MessageSize); + MmCommunicateBuffer = (UINT8 *)AllocatePages (RequiredPages); + + if (MmCommunicateBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG ((DEBUG_ERROR, "%a: Failed to allocate memory: %r\n", __func__, Status)); + return Status; + } + + // Zero the entire Communication Buffer + ZeroMem (MmCommunicateBuffer, (RequiredPages * EFI_PAGE_SIZE)); + + // + // Program all payload structure contents + // + MmVarGetNextVarHeader = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *)(MmCommunicateBuffer + MM_VARIABLE_COMM_BUFFER_OFFSET); + + // Variable GUID + CopyMem ((VOID *)&MmVarGetNextVarHeader->Guid, VariableGuid, sizeof (GUID)); + + // Program the maximal length of name we can accept. + MmVarGetNextVarHeader->NameSize = *VariableNameSize; + + // Populate incoming variable name + CopyMem ((VOID *)&MmVarGetNextVarHeader->Name, VariableName, MmVarGetNextVarHeader->NameSize); + + // Send the MM request using MmCommunicationPei + Status = PopulateHeaderAndCommunicate (MmCommunicateBuffer, MessageSize, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME); + if (EFI_ERROR (Status)) { + // We received an error from either communicate or Variable Service. + if (Status != EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_ERROR, "%a - Communite to MM for variable service errored: %r\n", __func__, Status)); + } + + goto Exit; + } + + Status = EFI_SUCCESS; + + // User provided buffer is too small + if (*VariableNameSize < MmVarGetNextVarHeader->NameSize) { + Status = EFI_BUFFER_TOO_SMALL; + } + +Exit: + // Update the name size to be returned + *VariableNameSize = MmVarGetNextVarHeader->NameSize; + + if (Status == EFI_SUCCESS) { + CopyMem ((VOID *)VariableName, (UINT8 *)MmVarGetNextVarHeader->Name, *VariableNameSize); + CopyMem ((VOID *)VariableGuid, (UINT8 *)&(MmVarGetNextVarHeader->Guid), sizeof (EFI_GUID)); + } + + // Free the Communication Buffer + if (MmCommunicateBuffer != NULL) { + FreePages (MmCommunicateBuffer, RequiredPages); + } + + return Status; +} diff --git a/MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.h b/MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.h new file mode 100644 index 0000000000..0feed8cd1c --- /dev/null +++ b/MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.h @@ -0,0 +1,134 @@ +/** @file -- MmVariablePei.h + Provides interface for reading Secure System Variables during PEI. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef PEI_MM_VARIABLE_LIB_H_ +#define PEI_MM_VARIABLE_LIB_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +/** + Entry point of PEI Secure Variable read driver + + @param FileHandle Handle of the file being invoked. + Type EFI_PEI_FILE_HANDLE is defined in FfsFindNextFile(). + @param PeiServices General purpose services available to every PEIM. + + @retval EFI_SUCCESS If the interface could be successfully installed + @retval Others Returned from PeiServicesInstallPpi() +**/ +EFI_STATUS +EFIAPI +PeiMmVariableInitialize ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ); + +/** + + This function enables the read of Secure Variables during PEI. + + This function is using the Secure Variable Store.If the Data + buffer is too small to hold the contents of the variable, the error + EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer + size to obtain the data. + + The function performs the following: + + 1) Creates an MM request + 2) Fills out the following data structures for the Secure Variable Service + SMM_VARIABLE_COMMUNICATE_HEADER/SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE + 3) Adds the MM data structures to the MM request. + 4) Sends the MM request to EL3 using MmCommunicationPeiLib. + 5) The MM request is sent to S-EL0. + 6) The MM request is then handled by the registered handler with GUID: gEfiSmmVariableProtocolGuid + + @param This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI. + @param VariableName A pointer to a null-terminated string that is the variable's name. + @param VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of + VariableGuid and VariableName must be unique. + @param Attributes If non-NULL, on return, points to the variable's attributes. + @param DataSize On entry, points to the size in bytes of the Data buffer. + On return, points to the size of the data returned in Data. + @param Data Points to the buffer which will hold the returned variable value. + May be NULL with a zero DataSize in order to determine the size of the buffer needed. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable was not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting data. + DataSize is updated with the size required for + the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or Data is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. + +**/ +EFI_STATUS +EFIAPI +PeiMmGetVariable ( + IN CONST EFI_PEI_READ_ONLY_VARIABLE2_PPI *This, + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VariableGuid, + OUT UINT32 *Attributes, + IN OUT UINTN *DataSize, + OUT VOID *Data OPTIONAL + ); + +/** + Return the next variable name and GUID. + + This function is called multiple times to retrieve the VariableName + and VariableGuid of all variables currently available in the system. + On each call, the previous results are passed into the interface, + and, on return, the interface returns the data for the next + interface. When the entire variable list has been returned, + EFI_NOT_FOUND is returned. + + @param This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI. + + @param VariableNameSize On entry, points to the size of the buffer pointed to by VariableName. + On return, the size of the variable name buffer. + @param VariableName On entry, a pointer to a null-terminated string that is the variable's name. + On return, points to the next variable's null-terminated name string. + + @param VariableGuid On entry, a pointer to an EFI_GUID that is the variable's GUID. + On return, a pointer to the next variable's GUID. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable could not be found. + @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the resulting + data. VariableNameSize is updated with the size + required for the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or + VariableNameSize is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. +**/ +EFI_STATUS +EFIAPI +PeiMmGetNextVariableName ( + IN CONST EFI_PEI_READ_ONLY_VARIABLE2_PPI *This, + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VariableGuid + ); + +#endif /* PEI_MM_VARIABLE_LIB_H_ */ diff --git a/MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.inf b/MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.inf new file mode 100644 index 0000000000..13e80523f9 --- /dev/null +++ b/MdeModulePkg/Universal/Variable/MmVariablePei/MmVariablePei.inf @@ -0,0 +1,40 @@ +## @file -- MmVariablePei.inf +# Provides interface for reading Secure System Variables during PEI. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MmVariablePei + FILE_GUID = CD660A87-454B-4346-A35C-3D89BF8ECFAF + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = PeiMmVariableInitialize + +[Sources] + MmVariablePei.c + MmVariablePei.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PcdLib + PeiServicesLib + PeimEntryPoint + MemoryAllocationLib + HobLib + +[Protocols] + gEfiSmmVariableProtocolGuid ## CONSUMES + +[Ppis] + gEfiPeiReadOnlyVariable2PpiGuid ## PRODUCES + gEfiPeiMmCommunicationPpiGuid ## CONSUMES + +[Depex] + gEfiPeiMmCommunicationPpiGuid