• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   This module collects performance data for SMM driver boot records and S3 Suspend Performance Record.
3 
4   This module registers report status code listener to collect performance data
5   for SMM driver boot records and S3 Suspend Performance Record.
6 
7   Caution: This module requires additional review when modified.
8   This driver will have external input - communicate buffer in SMM mode.
9   This external input must be validated carefully to avoid security issue like
10   buffer overflow, integer overflow.
11 
12   FpdtSmiHandler() will receive untrusted input and do basic validation.
13 
14   Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
15   This program and the accompanying materials
16   are licensed and made available under the terms and conditions of the BSD License
17   which accompanies this distribution.  The full text of the license may be found at
18   http://opensource.org/licenses/bsd-license.php
19 
20   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
21   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
22 
23 **/
24 
25 #include <PiSmm.h>
26 
27 #include <Protocol/SmmReportStatusCodeHandler.h>
28 
29 #include <Guid/FirmwarePerformance.h>
30 
31 #include <Library/SmmServicesTableLib.h>
32 #include <Library/BaseLib.h>
33 #include <Library/DebugLib.h>
34 #include <Library/TimerLib.h>
35 #include <Library/LockBoxLib.h>
36 #include <Library/PcdLib.h>
37 #include <Library/BaseMemoryLib.h>
38 #include <Library/MemoryAllocationLib.h>
39 #include <Library/UefiBootServicesTableLib.h>
40 #include <Library/SynchronizationLib.h>
41 #include <Library/SmmMemLib.h>
42 
43 #define EXTENSION_RECORD_SIZE     0x1000
44 
45 EFI_SMM_RSC_HANDLER_PROTOCOL  *mRscHandlerProtocol    = NULL;
46 UINT64                        mSuspendStartTime       = 0;
47 BOOLEAN                       mS3SuspendLockBoxSaved  = FALSE;
48 UINT32                        mBootRecordSize = 0;
49 UINT32                        mBootRecordMaxSize = 0;
50 UINT8                         *mBootRecordBuffer = NULL;
51 
52 SPIN_LOCK                     mSmmFpdtLock;
53 BOOLEAN                       mSmramIsOutOfResource = FALSE;
54 
55 /**
56   Report status code listener for SMM. This is used to record the performance
57   data for S3 Suspend Start and S3 Suspend End in FPDT.
58 
59   @param[in]  CodeType            Indicates the type of status code being reported.
60   @param[in]  Value               Describes the current status of a hardware or software entity.
61                                   This included information about the class and subclass that is used to
62                                   classify the entity as well as an operation.
63   @param[in]  Instance            The enumeration of a hardware or software entity within
64                                   the system. Valid instance numbers start with 1.
65   @param[in]  CallerId            This optional parameter may be used to identify the caller.
66                                   This parameter allows the status code driver to apply different rules to
67                                   different callers.
68   @param[in]  Data                This optional parameter may be used to pass additional data.
69 
70   @retval EFI_SUCCESS             Status code is what we expected.
71   @retval EFI_UNSUPPORTED         Status code not supported.
72 
73 **/
74 EFI_STATUS
75 EFIAPI
FpdtStatusCodeListenerSmm(IN EFI_STATUS_CODE_TYPE CodeType,IN EFI_STATUS_CODE_VALUE Value,IN UINT32 Instance,IN EFI_GUID * CallerId,IN EFI_STATUS_CODE_DATA * Data)76 FpdtStatusCodeListenerSmm (
77   IN EFI_STATUS_CODE_TYPE     CodeType,
78   IN EFI_STATUS_CODE_VALUE    Value,
79   IN UINT32                   Instance,
80   IN EFI_GUID                 *CallerId,
81   IN EFI_STATUS_CODE_DATA     *Data
82   )
83 {
84   EFI_STATUS                           Status;
85   UINT64                               CurrentTime;
86   EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD  S3SuspendRecord;
87   UINT8                                *NewRecordBuffer;
88 
89   //
90   // Check whether status code is what we are interested in.
91   //
92   if ((CodeType & EFI_STATUS_CODE_TYPE_MASK) != EFI_PROGRESS_CODE) {
93     return EFI_UNSUPPORTED;
94   }
95 
96   //
97   // Collect one or more Boot records in boot time
98   //
99   if (Data != NULL && CompareGuid (&Data->Type, &gEfiFirmwarePerformanceGuid)) {
100     AcquireSpinLock (&mSmmFpdtLock);
101 
102     if (mBootRecordSize + Data->Size > mBootRecordMaxSize) {
103       //
104       // Try to allocate big SMRAM data to store Boot record.
105       //
106       if (mSmramIsOutOfResource) {
107         ReleaseSpinLock (&mSmmFpdtLock);
108         return EFI_OUT_OF_RESOURCES;
109       }
110       NewRecordBuffer = ReallocatePool (mBootRecordSize, mBootRecordSize + Data->Size + EXTENSION_RECORD_SIZE, mBootRecordBuffer);
111       if (NewRecordBuffer == NULL) {
112         ReleaseSpinLock (&mSmmFpdtLock);
113         mSmramIsOutOfResource = TRUE;
114         return EFI_OUT_OF_RESOURCES;
115       }
116       mBootRecordBuffer  = NewRecordBuffer;
117       mBootRecordMaxSize = mBootRecordSize + Data->Size + EXTENSION_RECORD_SIZE;
118     }
119     //
120     // Save boot record into the temp memory space.
121     //
122     CopyMem (mBootRecordBuffer + mBootRecordSize, Data + 1, Data->Size);
123     mBootRecordSize += Data->Size;
124 
125     ReleaseSpinLock (&mSmmFpdtLock);
126     return EFI_SUCCESS;
127   }
128 
129   if ((Value != PcdGet32 (PcdProgressCodeS3SuspendStart)) &&
130       (Value != PcdGet32 (PcdProgressCodeS3SuspendEnd))) {
131     return EFI_UNSUPPORTED;
132   }
133 
134   //
135   // Retrieve current time.
136   //
137   CurrentTime = GetTimeInNanoSecond (GetPerformanceCounter ());
138 
139   if (Value == PcdGet32 (PcdProgressCodeS3SuspendStart)) {
140     //
141     // S3 Suspend started, record the performance data and return.
142     //
143     mSuspendStartTime = CurrentTime;
144     return EFI_SUCCESS;
145   }
146 
147   //
148   // We are going to S3 sleep, record S3 Suspend End performance data.
149   //
150   S3SuspendRecord.SuspendStart = mSuspendStartTime;
151   S3SuspendRecord.SuspendEnd   = CurrentTime;
152 
153   //
154   // Save S3 suspend performance data to lock box, it will be used by Firmware Performance PEIM.
155   //
156   if (!mS3SuspendLockBoxSaved) {
157     Status = SaveLockBox (
158                &gEfiFirmwarePerformanceGuid,
159                &S3SuspendRecord,
160                sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD)
161                );
162     ASSERT_EFI_ERROR (Status);
163 
164     mS3SuspendLockBoxSaved = TRUE;
165   } else {
166     Status = UpdateLockBox (
167                &gEfiFirmwarePerformanceGuid,
168                0,
169                &S3SuspendRecord,
170                sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD)
171                );
172     ASSERT_EFI_ERROR (Status);
173   }
174 
175   return EFI_SUCCESS;
176 }
177 
178 /**
179   Communication service SMI Handler entry.
180 
181   This SMI handler provides services for report SMM boot records.
182 
183   Caution: This function may receive untrusted input.
184   Communicate buffer and buffer size are external input, so this function will do basic validation.
185 
186   @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
187   @param[in]     RegisterContext Points to an optional handler context which was specified when the
188                                  handler was registered.
189   @param[in, out] CommBuffer     A pointer to a collection of data in memory that will
190                                  be conveyed from a non-SMM environment into an SMM environment.
191   @param[in, out] CommBufferSize The size of the CommBuffer.
192 
193   @retval EFI_SUCCESS                         The interrupt was handled and quiesced. No other handlers
194                                               should still be called.
195   @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other handlers should
196                                               still be called.
197   @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still
198                                               be called.
199   @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.
200 
201 **/
202 EFI_STATUS
203 EFIAPI
FpdtSmiHandler(IN EFI_HANDLE DispatchHandle,IN CONST VOID * RegisterContext,IN OUT VOID * CommBuffer,IN OUT UINTN * CommBufferSize)204 FpdtSmiHandler (
205   IN     EFI_HANDLE                   DispatchHandle,
206   IN     CONST VOID                   *RegisterContext,
207   IN OUT VOID                         *CommBuffer,
208   IN OUT UINTN                        *CommBufferSize
209   )
210 {
211   EFI_STATUS                   Status;
212   SMM_BOOT_RECORD_COMMUNICATE  *SmmCommData;
213   UINTN                        BootRecordSize;
214   VOID                         *BootRecordData;
215   UINTN                        TempCommBufferSize;
216 
217   //
218   // If input is invalid, stop processing this SMI
219   //
220   if (CommBuffer == NULL || CommBufferSize == NULL) {
221     return EFI_SUCCESS;
222   }
223 
224   TempCommBufferSize = *CommBufferSize;
225 
226   if(TempCommBufferSize < sizeof (SMM_BOOT_RECORD_COMMUNICATE)) {
227     return EFI_SUCCESS;
228   }
229 
230   if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
231     DEBUG ((EFI_D_ERROR, "FpdtSmiHandler: SMM communication data buffer in SMRAM or overflow!\n"));
232     return EFI_SUCCESS;
233   }
234 
235   SmmCommData = (SMM_BOOT_RECORD_COMMUNICATE*)CommBuffer;
236 
237   Status = EFI_SUCCESS;
238 
239   switch (SmmCommData->Function) {
240     case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE :
241        SmmCommData->BootRecordSize = mBootRecordSize;
242        break;
243 
244     case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA :
245        BootRecordData = SmmCommData->BootRecordData;
246        BootRecordSize = SmmCommData->BootRecordSize;
247        if (BootRecordData == NULL || BootRecordSize < mBootRecordSize) {
248          Status = EFI_INVALID_PARAMETER;
249          break;
250        }
251 
252        //
253        // Sanity check
254        //
255        SmmCommData->BootRecordSize = mBootRecordSize;
256        if (!SmmIsBufferOutsideSmmValid ((UINTN)BootRecordData, mBootRecordSize)) {
257          DEBUG ((EFI_D_ERROR, "FpdtSmiHandler: SMM Data buffer in SMRAM or overflow!\n"));
258          Status = EFI_ACCESS_DENIED;
259          break;
260        }
261 
262        CopyMem (
263          (UINT8*)BootRecordData,
264          mBootRecordBuffer,
265          mBootRecordSize
266          );
267        break;
268 
269     default:
270        Status = EFI_UNSUPPORTED;
271   }
272 
273   SmmCommData->ReturnStatus = Status;
274 
275   return EFI_SUCCESS;
276 }
277 
278 /**
279   The module Entry Point of the Firmware Performance Data Table SMM driver.
280 
281   @param[in]  ImageHandle    The firmware allocated handle for the EFI image.
282   @param[in]  SystemTable    A pointer to the EFI System Table.
283 
284   @retval EFI_SUCCESS    The entry point is executed successfully.
285   @retval Other          Some error occurs when executing this entry point.
286 
287 **/
288 EFI_STATUS
289 EFIAPI
FirmwarePerformanceSmmEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)290 FirmwarePerformanceSmmEntryPoint (
291   IN EFI_HANDLE          ImageHandle,
292   IN EFI_SYSTEM_TABLE    *SystemTable
293   )
294 {
295   EFI_STATUS                Status;
296   EFI_HANDLE                Handle;
297 
298   //
299   // Initialize spin lock
300   //
301   InitializeSpinLock (&mSmmFpdtLock);
302 
303   //
304   // Get SMM Report Status Code Handler Protocol.
305   //
306   Status = gSmst->SmmLocateProtocol (
307                     &gEfiSmmRscHandlerProtocolGuid,
308                     NULL,
309                     (VOID **) &mRscHandlerProtocol
310                     );
311   ASSERT_EFI_ERROR (Status);
312 
313   //
314   // Register report status code listener for BootRecords and S3 Suspend Start and End.
315   //
316   Status = mRscHandlerProtocol->Register (FpdtStatusCodeListenerSmm);
317   ASSERT_EFI_ERROR (Status);
318 
319   //
320   // Register SMI handler.
321   //
322   Handle = NULL;
323   Status = gSmst->SmiHandlerRegister (FpdtSmiHandler, &gEfiFirmwarePerformanceGuid, &Handle);
324   ASSERT_EFI_ERROR (Status);
325 
326   return Status;
327 }
328