/** @file | |
PiSmmCommunication PEI Driver. | |
Copyright (c) 2010 - 2021, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiPei.h> | |
#include <PiDxe.h> | |
#include <PiSmm.h> | |
#include <Library/PeiServicesTablePointerLib.h> | |
#include <Library/PeiServicesLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/HobLib.h> | |
#include <Library/DebugLib.h> | |
#include <Protocol/SmmCommunication.h> | |
#include <Ppi/SmmCommunication.h> | |
#include <Ppi/SmmAccess.h> | |
#include <Ppi/SmmControl.h> | |
#include <Guid/AcpiS3Context.h> | |
#include "PiSmmCommunicationPrivate.h" | |
/** | |
the whole picture is below: | |
+----------------------------------+ | |
| ACPI_VARIABLE_HOB | | |
| SmramDescriptor | <- DRAM | |
| CpuStart |--- | |
+----------------------------------+ | | |
| | |
+----------------------------------+<-- | |
| SMM_S3_RESUME_STATE | | |
| Signature | <- SMRAM | |
| Smst |--- | |
+----------------------------------+ | | |
| | |
+----------------------------------+<-- | |
| EFI_SMM_SYSTEM_TABLE2 | | |
| NumberOfTableEntries | <- SMRAM | |
| SmmConfigurationTable |--- | |
+----------------------------------+ | | |
| | |
+----------------------------------+<-- | |
| EFI_SMM_COMMUNICATION_CONTEXT | | |
| SwSmiNumber | <- SMRAM | |
| BufferPtrAddress |--- | |
+----------------------------------+ | | |
| | |
+----------------------------------+<-- | |
| Communication Buffer Pointer | <- AcpiNvs | |
+----------------------------------+--- | |
| | |
+----------------------------------+<-- | |
| EFI_SMM_COMMUNICATE_HEADER | | |
| HeaderGuid | <- DRAM | |
| MessageLength | | |
+----------------------------------+ | |
**/ | |
#if defined (MDE_CPU_IA32) | |
typedef struct { | |
EFI_TABLE_HEADER Hdr; | |
UINT64 SmmFirmwareVendor; | |
UINT64 SmmFirmwareRevision; | |
UINT64 SmmInstallConfigurationTable; | |
UINT64 SmmIoMemRead; | |
UINT64 SmmIoMemWrite; | |
UINT64 SmmIoIoRead; | |
UINT64 SmmIoIoWrite; | |
UINT64 SmmAllocatePool; | |
UINT64 SmmFreePool; | |
UINT64 SmmAllocatePages; | |
UINT64 SmmFreePages; | |
UINT64 SmmStartupThisAp; | |
UINT64 CurrentlyExecutingCpu; | |
UINT64 NumberOfCpus; | |
UINT64 CpuSaveStateSize; | |
UINT64 CpuSaveState; | |
UINT64 NumberOfTableEntries; | |
UINT64 SmmConfigurationTable; | |
} EFI_SMM_SYSTEM_TABLE2_64; | |
typedef struct { | |
EFI_GUID VendorGuid; | |
UINT64 VendorTable; | |
} EFI_CONFIGURATION_TABLE64; | |
#endif | |
#if defined (MDE_CPU_X64) | |
typedef EFI_SMM_SYSTEM_TABLE2 EFI_SMM_SYSTEM_TABLE2_64; | |
typedef EFI_CONFIGURATION_TABLE EFI_CONFIGURATION_TABLE64; | |
#endif | |
/** | |
Communicates with a registered handler. | |
This function provides a service to send and receive messages from a registered UEFI service. | |
@param[in] This The EFI_PEI_SMM_COMMUNICATION_PPI instance. | |
@param[in, out] CommBuffer A pointer to the buffer to convey into SMRAM. | |
@param[in, out] CommSize The size of the data buffer being passed in.On exit, the size of data | |
being returned. Zero if the handler does not wish to reply with any data. | |
@retval EFI_SUCCESS The message was successfully posted. | |
@retval EFI_INVALID_PARAMETER The CommBuffer was NULL. | |
@retval EFI_NOT_STARTED The service is NOT started. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Communicate ( | |
IN CONST EFI_PEI_SMM_COMMUNICATION_PPI *This, | |
IN OUT VOID *CommBuffer, | |
IN OUT UINTN *CommSize | |
); | |
EFI_PEI_SMM_COMMUNICATION_PPI mSmmCommunicationPpi = { Communicate }; | |
EFI_PEI_PPI_DESCRIPTOR mPpiList = { | |
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
&gEfiPeiSmmCommunicationPpiGuid, | |
&mSmmCommunicationPpi | |
}; | |
/** | |
Get SMM communication context. | |
@return SMM communication context. | |
**/ | |
EFI_SMM_COMMUNICATION_CONTEXT * | |
GetCommunicationContext ( | |
VOID | |
) | |
{ | |
EFI_HOB_GUID_TYPE *GuidHob; | |
EFI_SMM_COMMUNICATION_CONTEXT *SmmCommunicationContext; | |
GuidHob = GetFirstGuidHob (&gEfiPeiSmmCommunicationPpiGuid); | |
ASSERT (GuidHob != NULL); | |
SmmCommunicationContext = (EFI_SMM_COMMUNICATION_CONTEXT *)GET_GUID_HOB_DATA (GuidHob); | |
return SmmCommunicationContext; | |
} | |
/** | |
Set SMM communication context. | |
@param SmmCommunicationContext SMM communication context. | |
**/ | |
VOID | |
SetCommunicationContext ( | |
IN EFI_SMM_COMMUNICATION_CONTEXT *SmmCommunicationContext | |
) | |
{ | |
EFI_PEI_HOB_POINTERS Hob; | |
UINTN BufferSize; | |
BufferSize = sizeof (*SmmCommunicationContext); | |
Hob.Raw = BuildGuidHob ( | |
&gEfiPeiSmmCommunicationPpiGuid, | |
BufferSize | |
); | |
ASSERT (Hob.Raw); | |
CopyMem ((VOID *)Hob.Raw, SmmCommunicationContext, sizeof (*SmmCommunicationContext)); | |
} | |
/** | |
Get VendorTable by VendorGuid in Smst. | |
@param Signature Signature of SMM_S3_RESUME_STATE | |
@param Smst SMM system table | |
@param VendorGuid vendor guid | |
@return vendor table. | |
**/ | |
VOID * | |
InternalSmstGetVendorTableByGuid ( | |
IN UINT64 Signature, | |
IN EFI_SMM_SYSTEM_TABLE2 *Smst, | |
IN EFI_GUID *VendorGuid | |
) | |
{ | |
EFI_CONFIGURATION_TABLE *SmmConfigurationTable; | |
UINTN NumberOfTableEntries; | |
UINTN Index; | |
EFI_SMM_SYSTEM_TABLE2_64 *Smst64; | |
EFI_CONFIGURATION_TABLE64 *SmmConfigurationTable64; | |
if ((sizeof (UINTN) == sizeof (UINT32)) && (Signature == SMM_S3_RESUME_SMM_64)) { | |
// | |
// 32 PEI + 64 DXE | |
// | |
Smst64 = (EFI_SMM_SYSTEM_TABLE2_64 *)Smst; | |
DEBUG ((DEBUG_INFO, "InitCommunicationContext - SmmConfigurationTable: %x\n", Smst64->SmmConfigurationTable)); | |
DEBUG ((DEBUG_INFO, "InitCommunicationContext - NumberOfTableEntries: %x\n", Smst64->NumberOfTableEntries)); | |
SmmConfigurationTable64 = (EFI_CONFIGURATION_TABLE64 *)(UINTN)Smst64->SmmConfigurationTable; | |
NumberOfTableEntries = (UINTN)Smst64->NumberOfTableEntries; | |
for (Index = 0; Index < NumberOfTableEntries; Index++) { | |
if (CompareGuid (&SmmConfigurationTable64[Index].VendorGuid, VendorGuid)) { | |
return (VOID *)(UINTN)SmmConfigurationTable64[Index].VendorTable; | |
} | |
} | |
return NULL; | |
} else { | |
DEBUG ((DEBUG_INFO, "InitCommunicationContext - SmmConfigurationTable: %x\n", Smst->SmmConfigurationTable)); | |
DEBUG ((DEBUG_INFO, "InitCommunicationContext - NumberOfTableEntries: %x\n", Smst->NumberOfTableEntries)); | |
SmmConfigurationTable = Smst->SmmConfigurationTable; | |
NumberOfTableEntries = Smst->NumberOfTableEntries; | |
for (Index = 0; Index < NumberOfTableEntries; Index++) { | |
if (CompareGuid (&SmmConfigurationTable[Index].VendorGuid, VendorGuid)) { | |
return (VOID *)SmmConfigurationTable[Index].VendorTable; | |
} | |
} | |
return NULL; | |
} | |
} | |
/** | |
Init SMM communication context. | |
**/ | |
VOID | |
InitCommunicationContext ( | |
VOID | |
) | |
{ | |
EFI_SMRAM_DESCRIPTOR *SmramDescriptor; | |
SMM_S3_RESUME_STATE *SmmS3ResumeState; | |
VOID *GuidHob; | |
EFI_SMM_COMMUNICATION_CONTEXT *SmmCommunicationContext; | |
GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid); | |
ASSERT (GuidHob != NULL); | |
SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *)GET_GUID_HOB_DATA (GuidHob); | |
SmmS3ResumeState = (SMM_S3_RESUME_STATE *)(UINTN)SmramDescriptor->CpuStart; | |
DEBUG ((DEBUG_INFO, "InitCommunicationContext - SmmS3ResumeState: %x\n", SmmS3ResumeState)); | |
DEBUG ((DEBUG_INFO, "InitCommunicationContext - Smst: %x\n", SmmS3ResumeState->Smst)); | |
SmmCommunicationContext = (EFI_SMM_COMMUNICATION_CONTEXT *)InternalSmstGetVendorTableByGuid ( | |
SmmS3ResumeState->Signature, | |
(EFI_SMM_SYSTEM_TABLE2 *)(UINTN)SmmS3ResumeState->Smst, | |
&gEfiPeiSmmCommunicationPpiGuid | |
); | |
ASSERT (SmmCommunicationContext != NULL); | |
SetCommunicationContext (SmmCommunicationContext); | |
return; | |
} | |
/** | |
Communicates with a registered handler. | |
This function provides a service to send and receive messages from a registered UEFI service. | |
@param[in] This The EFI_PEI_SMM_COMMUNICATION_PPI instance. | |
@param[in, out] CommBuffer A pointer to the buffer to convey into SMRAM. | |
@param[in, out] CommSize The size of the data buffer being passed in.On exit, the size of data | |
being returned. Zero if the handler does not wish to reply with any data. | |
@retval EFI_SUCCESS The message was successfully posted. | |
@retval EFI_INVALID_PARAMETER The CommBuffer was NULL. | |
@retval EFI_NOT_STARTED The service is NOT started. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Communicate ( | |
IN CONST EFI_PEI_SMM_COMMUNICATION_PPI *This, | |
IN OUT VOID *CommBuffer, | |
IN OUT UINTN *CommSize | |
) | |
{ | |
EFI_STATUS Status; | |
PEI_SMM_CONTROL_PPI *SmmControl; | |
PEI_SMM_ACCESS_PPI *SmmAccess; | |
UINT8 SmiCommand; | |
UINTN Size; | |
EFI_SMM_COMMUNICATION_CONTEXT *SmmCommunicationContext; | |
DEBUG ((DEBUG_INFO, "PiSmmCommunicationPei Communicate Enter\n")); | |
if (CommBuffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Get needed resource | |
// | |
Status = PeiServicesLocatePpi ( | |
&gPeiSmmControlPpiGuid, | |
0, | |
NULL, | |
(VOID **)&SmmControl | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_NOT_STARTED; | |
} | |
Status = PeiServicesLocatePpi ( | |
&gPeiSmmAccessPpiGuid, | |
0, | |
NULL, | |
(VOID **)&SmmAccess | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_NOT_STARTED; | |
} | |
// | |
// Check SMRAM locked, it should be done after SMRAM lock. | |
// | |
if (!SmmAccess->LockState) { | |
DEBUG ((DEBUG_INFO, "PiSmmCommunicationPei LockState - %x\n", (UINTN)SmmAccess->LockState)); | |
return EFI_NOT_STARTED; | |
} | |
SmmCommunicationContext = GetCommunicationContext (); | |
DEBUG ((DEBUG_INFO, "PiSmmCommunicationPei BufferPtrAddress - 0x%016lx, BufferPtr: 0x%016lx\n", SmmCommunicationContext->BufferPtrAddress, *(EFI_PHYSICAL_ADDRESS *)(UINTN)SmmCommunicationContext->BufferPtrAddress)); | |
// | |
// No need to check if BufferPtr is 0, because it is in PEI phase. | |
// | |
*(EFI_PHYSICAL_ADDRESS *)(UINTN)SmmCommunicationContext->BufferPtrAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CommBuffer; | |
DEBUG ((DEBUG_INFO, "PiSmmCommunicationPei CommBuffer - %x\n", (UINTN)CommBuffer)); | |
// | |
// Send command | |
// | |
SmiCommand = (UINT8)SmmCommunicationContext->SwSmiNumber; | |
Size = sizeof (SmiCommand); | |
Status = SmmControl->Trigger ( | |
(EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), | |
SmmControl, | |
(INT8 *)&SmiCommand, | |
&Size, | |
FALSE, | |
0 | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Setting BufferPtr to 0 means this transaction is done. | |
// | |
*(EFI_PHYSICAL_ADDRESS *)(UINTN)SmmCommunicationContext->BufferPtrAddress = 0; | |
DEBUG ((DEBUG_INFO, "PiSmmCommunicationPei Communicate Exit\n")); | |
return EFI_SUCCESS; | |
} | |
/** | |
Entry Point for PI SMM communication PEIM. | |
@param FileHandle Handle of the file being invoked. | |
@param PeiServices Pointer to PEI Services table. | |
@retval EFI_SUCCESS | |
@return Others Some error occurs. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PiSmmCommunicationPeiEntryPoint ( | |
IN EFI_PEI_FILE_HANDLE FileHandle, | |
IN CONST EFI_PEI_SERVICES **PeiServices | |
) | |
{ | |
EFI_STATUS Status; | |
PEI_SMM_ACCESS_PPI *SmmAccess; | |
EFI_BOOT_MODE BootMode; | |
UINTN Index; | |
BootMode = GetBootModeHob (); | |
if (BootMode != BOOT_ON_S3_RESUME) { | |
return EFI_UNSUPPORTED; | |
} | |
Status = PeiServicesLocatePpi ( | |
&gPeiSmmAccessPpiGuid, | |
0, | |
NULL, | |
(VOID **)&SmmAccess | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_NOT_STARTED; | |
} | |
// | |
// Check SMRAM locked, it should be done before SMRAM lock. | |
// | |
if (SmmAccess->LockState) { | |
DEBUG ((DEBUG_INFO, "PiSmmCommunicationPei LockState - %x\n", (UINTN)SmmAccess->LockState)); | |
return EFI_ACCESS_DENIED; | |
} | |
// | |
// Open all SMRAM | |
// | |
for (Index = 0; !EFI_ERROR (Status); Index++) { | |
Status = SmmAccess->Open ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index); | |
} | |
InitCommunicationContext (); | |
PeiServicesInstallPpi (&mPpiList); | |
return RETURN_SUCCESS; | |
} |