/** @file\r
- This module update S3 Suspend Performance Record in ACPI Firmware Performance Data Table.\r
+ This module collects performance data for SMM driver boot records and S3 Suspend Performance Record.\r
\r
- This module register report status code listener to collect performance data\r
- for S3 Suspend Performance Record.\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
- Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
- This program and the accompanying materials\r
- are licensed and made available under the terms and conditions of the BSD License\r
- which accompanies this distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\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
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\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 <IndustryStandard/Acpi50.h>\r
-\r
#include <Protocol/SmmReportStatusCodeHandler.h>\r
\r
#include <Guid/FirmwarePerformance.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
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
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
IN EFI_SYSTEM_TABLE *SystemTable\r
)\r
{\r
- if (FeaturePcdGet (PcdFirmwarePerformanceDataTableS3Support)) {\r
- EFI_STATUS Status;\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\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
+ // Initialize spin lock\r
+ //\r
+ InitializeSpinLock (&mSmmFpdtLock);\r
\r
- //\r
- // Register report status code listener for S3 Suspend Start and End.\r
- //\r
- Status = mRscHandlerProtocol->Register (FpdtStatusCodeListenerSmm);\r
- ASSERT_EFI_ERROR (Status);\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
- return Status;\r
- } else {\r
- return EFI_UNSUPPORTED;\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