-/** @file\r
- This module collects performance data for SMM driver boot records and S3 Suspend Performance Record.\r
-\r
- This module registers report status code listener to collect performance data\r
- for SMM driver boot records and S3 Suspend Performance Record.\r
-\r
- Caution: This module requires additional review when modified.\r
- This driver will have external input - communicate buffer in SMM mode.\r
- This external input must be validated carefully to avoid security issue like\r
- buffer overflow, integer overflow.\r
-\r
- FpdtSmiHandler() will receive untrusted input and do basic validation.\r
-\r
- Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
- SPDX-License-Identifier: BSD-2-Clause-Patent\r
-\r
-**/\r
-\r
-#include <PiSmm.h>\r
-\r
-#include <Protocol/SmmReportStatusCodeHandler.h>\r
-\r
-#include <Guid/FirmwarePerformance.h>\r
-\r
-#include <Library/SmmServicesTableLib.h>\r
-#include <Library/BaseLib.h>\r
-#include <Library/DebugLib.h>\r
-#include <Library/TimerLib.h>\r
-#include <Library/LockBoxLib.h>\r
-#include <Library/PcdLib.h>\r
-#include <Library/BaseMemoryLib.h>\r
-#include <Library/MemoryAllocationLib.h>\r
-#include <Library/UefiBootServicesTableLib.h>\r
-#include <Library/SynchronizationLib.h>\r
-#include <Library/SmmMemLib.h>\r
-\r
-SMM_BOOT_PERFORMANCE_TABLE *mSmmBootPerformanceTable = NULL;\r
-\r
-EFI_SMM_RSC_HANDLER_PROTOCOL *mRscHandlerProtocol = NULL;\r
-UINT64 mSuspendStartTime = 0;\r
-BOOLEAN mS3SuspendLockBoxSaved = FALSE;\r
-UINT32 mBootRecordSize = 0;\r
-UINT8 *mBootRecordBuffer = NULL;\r
-\r
-SPIN_LOCK mSmmFpdtLock;\r
-BOOLEAN mSmramIsOutOfResource = FALSE;\r
-\r
-/**\r
- Report status code listener for SMM. This is used to record the performance\r
- data for S3 Suspend Start and S3 Suspend End in FPDT.\r
-\r
- @param[in] CodeType Indicates the type of status code being reported.\r
- @param[in] Value Describes the current status of a hardware or software entity.\r
- This included information about the class and subclass that is used to\r
- classify the entity as well as an operation.\r
- @param[in] Instance The enumeration of a hardware or software entity within\r
- the system. Valid instance numbers start with 1.\r
- @param[in] CallerId This optional parameter may be used to identify the caller.\r
- This parameter allows the status code driver to apply different rules to\r
- different callers.\r
- @param[in] Data This optional parameter may be used to pass additional data.\r
-\r
- @retval EFI_SUCCESS Status code is what we expected.\r
- @retval EFI_UNSUPPORTED Status code not supported.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-FpdtStatusCodeListenerSmm (\r
- IN EFI_STATUS_CODE_TYPE CodeType,\r
- IN EFI_STATUS_CODE_VALUE Value,\r
- IN UINT32 Instance,\r
- IN EFI_GUID *CallerId,\r
- IN EFI_STATUS_CODE_DATA *Data\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINT64 CurrentTime;\r
- EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD S3SuspendRecord;\r
-\r
- //\r
- // Check whether status code is what we are interested in.\r
- //\r
- if ((CodeType & EFI_STATUS_CODE_TYPE_MASK) != EFI_PROGRESS_CODE) {\r
- return EFI_UNSUPPORTED;\r
- }\r
-\r
- //\r
- // Collect one or more Boot records in boot time\r
- //\r
- if (Data != NULL && CompareGuid (&Data->Type, &gEdkiiFpdtExtendedFirmwarePerformanceGuid)) {\r
- AcquireSpinLock (&mSmmFpdtLock);\r
- //\r
- // Get the boot performance data.\r
- //\r
- CopyMem (&mSmmBootPerformanceTable, Data + 1, Data->Size);\r
- mBootRecordBuffer = ((UINT8 *) (mSmmBootPerformanceTable)) + sizeof (SMM_BOOT_PERFORMANCE_TABLE);\r
-\r
- ReleaseSpinLock (&mSmmFpdtLock);\r
- return EFI_SUCCESS;\r
- }\r
-\r
- if (Data != NULL && CompareGuid (&Data->Type, &gEfiFirmwarePerformanceGuid)) {\r
- DEBUG ((DEBUG_ERROR, "FpdtStatusCodeListenerSmm: Performance data reported through gEfiFirmwarePerformanceGuid will not be collected by FirmwarePerformanceDataTableSmm\n"));\r
- return EFI_UNSUPPORTED;\r
- }\r
-\r
- if ((Value != PcdGet32 (PcdProgressCodeS3SuspendStart)) &&\r
- (Value != PcdGet32 (PcdProgressCodeS3SuspendEnd))) {\r
- return EFI_UNSUPPORTED;\r
- }\r
-\r
- //\r
- // Retrieve current time.\r
- //\r
- CurrentTime = GetTimeInNanoSecond (GetPerformanceCounter ());\r
-\r
- if (Value == PcdGet32 (PcdProgressCodeS3SuspendStart)) {\r
- //\r
- // S3 Suspend started, record the performance data and return.\r
- //\r
- mSuspendStartTime = CurrentTime;\r
- return EFI_SUCCESS;\r
- }\r
-\r
- //\r
- // We are going to S3 sleep, record S3 Suspend End performance data.\r
- //\r
- S3SuspendRecord.SuspendStart = mSuspendStartTime;\r
- S3SuspendRecord.SuspendEnd = CurrentTime;\r
-\r
- //\r
- // Save S3 suspend performance data to lock box, it will be used by Firmware Performance PEIM.\r
- //\r
- if (!mS3SuspendLockBoxSaved) {\r
- Status = SaveLockBox (\r
- &gEfiFirmwarePerformanceGuid,\r
- &S3SuspendRecord,\r
- sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD)\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- mS3SuspendLockBoxSaved = TRUE;\r
- } else {\r
- Status = UpdateLockBox (\r
- &gEfiFirmwarePerformanceGuid,\r
- 0,\r
- &S3SuspendRecord,\r
- sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD)\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Communication service SMI Handler entry.\r
-\r
- This SMI handler provides services for report SMM boot records.\r
-\r
- Caution: This function may receive untrusted input.\r
- Communicate buffer and buffer size are external input, so this function will do basic validation.\r
-\r
- @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().\r
- @param[in] RegisterContext Points to an optional handler context which was specified when the\r
- handler was registered.\r
- @param[in, out] CommBuffer A pointer to a collection of data in memory that will\r
- be conveyed from a non-SMM environment into an SMM environment.\r
- @param[in, out] CommBufferSize The size of the CommBuffer.\r
-\r
- @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers\r
- should still be called.\r
- @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should\r
- still be called.\r
- @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still\r
- be called.\r
- @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-FpdtSmiHandler (\r
- IN EFI_HANDLE DispatchHandle,\r
- IN CONST VOID *RegisterContext,\r
- IN OUT VOID *CommBuffer,\r
- IN OUT UINTN *CommBufferSize\r
- )\r
-{\r
- EFI_STATUS Status;\r
- SMM_BOOT_RECORD_COMMUNICATE *SmmCommData;\r
- UINTN BootRecordOffset;\r
- UINTN BootRecordSize;\r
- VOID *BootRecordData;\r
- UINTN TempCommBufferSize;\r
-\r
- //\r
- // If input is invalid, stop processing this SMI\r
- //\r
- if (CommBuffer == NULL || CommBufferSize == NULL) {\r
- return EFI_SUCCESS;\r
- }\r
-\r
- TempCommBufferSize = *CommBufferSize;\r
-\r
- if(TempCommBufferSize < sizeof (SMM_BOOT_RECORD_COMMUNICATE)) {\r
- return EFI_SUCCESS;\r
- }\r
-\r
- if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {\r
- DEBUG ((EFI_D_ERROR, "FpdtSmiHandler: SMM communication data buffer in SMRAM or overflow!\n"));\r
- return EFI_SUCCESS;\r
- }\r
-\r
- SmmCommData = (SMM_BOOT_RECORD_COMMUNICATE*)CommBuffer;\r
-\r
- Status = EFI_SUCCESS;\r
-\r
- switch (SmmCommData->Function) {\r
- case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE :\r
- if (mSmmBootPerformanceTable != NULL) {\r
- mBootRecordSize = mSmmBootPerformanceTable->Header.Length - sizeof (SMM_BOOT_PERFORMANCE_TABLE);\r
- }\r
- SmmCommData->BootRecordSize = mBootRecordSize;\r
- break;\r
-\r
- case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA :\r
- Status = EFI_UNSUPPORTED;\r
- break;\r
-\r
- case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA_BY_OFFSET :\r
- BootRecordOffset = SmmCommData->BootRecordOffset;\r
- BootRecordData = SmmCommData->BootRecordData;\r
- BootRecordSize = SmmCommData->BootRecordSize;\r
- if (BootRecordData == NULL || BootRecordOffset >= mBootRecordSize) {\r
- Status = EFI_INVALID_PARAMETER;\r
- break;\r
- }\r
-\r
- //\r
- // Sanity check\r
- //\r
- if (BootRecordSize > mBootRecordSize - BootRecordOffset) {\r
- BootRecordSize = mBootRecordSize - BootRecordOffset;\r
- }\r
- SmmCommData->BootRecordSize = BootRecordSize;\r
- if (!SmmIsBufferOutsideSmmValid ((UINTN)BootRecordData, BootRecordSize)) {\r
- DEBUG ((EFI_D_ERROR, "FpdtSmiHandler: SMM Data buffer in SMRAM or overflow!\n"));\r
- Status = EFI_ACCESS_DENIED;\r
- break;\r
- }\r
-\r
- CopyMem (\r
- (UINT8*)BootRecordData,\r
- mBootRecordBuffer + BootRecordOffset,\r
- BootRecordSize\r
- );\r
- break;\r
-\r
- default:\r
- Status = EFI_UNSUPPORTED;\r
- }\r
-\r
- SmmCommData->ReturnStatus = Status;\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- The module Entry Point of the Firmware Performance Data Table SMM driver.\r
-\r
- @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
- @param[in] SystemTable A pointer to the EFI System Table.\r
-\r
- @retval EFI_SUCCESS The entry point is executed successfully.\r
- @retval Other Some error occurs when executing this entry point.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-FirmwarePerformanceSmmEntryPoint (\r
- IN EFI_HANDLE ImageHandle,\r
- IN EFI_SYSTEM_TABLE *SystemTable\r
- )\r
-{\r
- EFI_STATUS Status;\r
- EFI_HANDLE Handle;\r
-\r
- //\r
- // Initialize spin lock\r
- //\r
- InitializeSpinLock (&mSmmFpdtLock);\r
-\r
- //\r
- // Get SMM Report Status Code Handler Protocol.\r
- //\r
- Status = gSmst->SmmLocateProtocol (\r
- &gEfiSmmRscHandlerProtocolGuid,\r
- NULL,\r
- (VOID **) &mRscHandlerProtocol\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- //\r
- // Register report status code listener for BootRecords and S3 Suspend Start and End.\r
- //\r
- Status = mRscHandlerProtocol->Register (FpdtStatusCodeListenerSmm);\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- //\r
- // Register SMI handler.\r
- //\r
- Handle = NULL;\r
- Status = gSmst->SmiHandlerRegister (FpdtSmiHandler, &gEfiFirmwarePerformanceGuid, &Handle);\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- return Status;\r
-}\r