/** @file PiSmmCommunication PEI Driver. Copyright (c) 2010 - 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }