• 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 - 2016, 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                        BootRecordOffset;
214   UINTN                        BootRecordSize;
215   VOID                         *BootRecordData;
216   UINTN                        TempCommBufferSize;
217 
218   //
219   // If input is invalid, stop processing this SMI
220   //
221   if (CommBuffer == NULL || CommBufferSize == NULL) {
222     return EFI_SUCCESS;
223   }
224 
225   TempCommBufferSize = *CommBufferSize;
226 
227   if(TempCommBufferSize < sizeof (SMM_BOOT_RECORD_COMMUNICATE)) {
228     return EFI_SUCCESS;
229   }
230 
231   if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
232     DEBUG ((EFI_D_ERROR, "FpdtSmiHandler: SMM communication data buffer in SMRAM or overflow!\n"));
233     return EFI_SUCCESS;
234   }
235 
236   SmmCommData = (SMM_BOOT_RECORD_COMMUNICATE*)CommBuffer;
237 
238   Status = EFI_SUCCESS;
239 
240   switch (SmmCommData->Function) {
241     case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE :
242       SmmCommData->BootRecordSize = mBootRecordSize;
243       break;
244 
245     case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA :
246       Status = EFI_UNSUPPORTED;
247       break;
248 
249     case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA_BY_OFFSET :
250       BootRecordOffset = SmmCommData->BootRecordOffset;
251       BootRecordData   = SmmCommData->BootRecordData;
252       BootRecordSize   = SmmCommData->BootRecordSize;
253       if (BootRecordData == NULL || BootRecordOffset >= mBootRecordSize) {
254         Status = EFI_INVALID_PARAMETER;
255         break;
256       }
257 
258       //
259       // Sanity check
260       //
261       if (BootRecordSize > mBootRecordSize - BootRecordOffset) {
262         BootRecordSize = mBootRecordSize - BootRecordOffset;
263       }
264       SmmCommData->BootRecordSize = BootRecordSize;
265       if (!SmmIsBufferOutsideSmmValid ((UINTN)BootRecordData, BootRecordSize)) {
266         DEBUG ((EFI_D_ERROR, "FpdtSmiHandler: SMM Data buffer in SMRAM or overflow!\n"));
267         Status = EFI_ACCESS_DENIED;
268         break;
269       }
270 
271       CopyMem (
272        (UINT8*)BootRecordData,
273        mBootRecordBuffer + BootRecordOffset,
274        BootRecordSize
275        );
276       break;
277 
278     default:
279       Status = EFI_UNSUPPORTED;
280   }
281 
282   SmmCommData->ReturnStatus = Status;
283 
284   return EFI_SUCCESS;
285 }
286 
287 /**
288   The module Entry Point of the Firmware Performance Data Table SMM driver.
289 
290   @param[in]  ImageHandle    The firmware allocated handle for the EFI image.
291   @param[in]  SystemTable    A pointer to the EFI System Table.
292 
293   @retval EFI_SUCCESS    The entry point is executed successfully.
294   @retval Other          Some error occurs when executing this entry point.
295 
296 **/
297 EFI_STATUS
298 EFIAPI
FirmwarePerformanceSmmEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)299 FirmwarePerformanceSmmEntryPoint (
300   IN EFI_HANDLE          ImageHandle,
301   IN EFI_SYSTEM_TABLE    *SystemTable
302   )
303 {
304   EFI_STATUS                Status;
305   EFI_HANDLE                Handle;
306 
307   //
308   // Initialize spin lock
309   //
310   InitializeSpinLock (&mSmmFpdtLock);
311 
312   //
313   // Get SMM Report Status Code Handler Protocol.
314   //
315   Status = gSmst->SmmLocateProtocol (
316                     &gEfiSmmRscHandlerProtocolGuid,
317                     NULL,
318                     (VOID **) &mRscHandlerProtocol
319                     );
320   ASSERT_EFI_ERROR (Status);
321 
322   //
323   // Register report status code listener for BootRecords and S3 Suspend Start and End.
324   //
325   Status = mRscHandlerProtocol->Register (FpdtStatusCodeListenerSmm);
326   ASSERT_EFI_ERROR (Status);
327 
328   //
329   // Register SMI handler.
330   //
331   Handle = NULL;
332   Status = gSmst->SmiHandlerRegister (FpdtSmiHandler, &gEfiFirmwarePerformanceGuid, &Handle);
333   ASSERT_EFI_ERROR (Status);
334 
335   return Status;
336 }
337