1 /** @file
2   This module install ACPI Firmware Performance Data Table (FPDT).
3 
4   This module register report status code listener to collect performance data
5   for Firmware Basic Boot Performance Record and other boot performance records,
6   and install FPDT to ACPI table.
7 
8   Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
9   This program and the accompanying materials
10   are licensed and made available under the terms and conditions of the BSD License
11   which accompanies this distribution.  The full text of the license may be found at
12   http://opensource.org/licenses/bsd-license.php
13 
14   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
15   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 
17 **/
18 
19 #include <PiDxe.h>
20 
21 #include <Protocol/ReportStatusCodeHandler.h>
22 #include <Protocol/AcpiTable.h>
23 #include <Protocol/SmmCommunication.h>
24 #include <Protocol/LockBox.h>
25 #include <Protocol/Variable.h>
26 
27 #include <Guid/Acpi.h>
28 #include <Guid/FirmwarePerformance.h>
29 #include <Guid/EventGroup.h>
30 #include <Guid/EventLegacyBios.h>
31 
32 #include <Library/UefiBootServicesTableLib.h>
33 #include <Library/UefiRuntimeServicesTableLib.h>
34 #include <Library/BaseLib.h>
35 #include <Library/DebugLib.h>
36 #include <Library/TimerLib.h>
37 #include <Library/BaseMemoryLib.h>
38 #include <Library/MemoryAllocationLib.h>
39 #include <Library/PcdLib.h>
40 #include <Library/HobLib.h>
41 #include <Library/LockBoxLib.h>
42 #include <Library/UefiLib.h>
43 
44 #define EXTENSION_RECORD_SIZE     0x10000
45 #define SMM_BOOT_RECORD_COMM_SIZE OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data) + sizeof(SMM_BOOT_RECORD_COMMUNICATE)
46 
47 EFI_RSC_HANDLER_PROTOCOL    *mRscHandlerProtocol = NULL;
48 
49 BOOLEAN                     mLockBoxReady = FALSE;
50 EFI_EVENT                   mReadyToBootEvent;
51 EFI_EVENT                   mLegacyBootEvent;
52 EFI_EVENT                   mExitBootServicesEvent;
53 UINTN                       mFirmwarePerformanceTableTemplateKey  = 0;
54 UINT32                      mBootRecordSize = 0;
55 UINT32                      mBootRecordMaxSize = 0;
56 UINT8                       *mBootRecordBuffer = NULL;
57 BOOLEAN                     mDxeCoreReportStatusCodeEnable = FALSE;
58 
59 BOOT_PERFORMANCE_TABLE                      *mAcpiBootPerformanceTable = NULL;
60 S3_PERFORMANCE_TABLE                        *mAcpiS3PerformanceTable   = NULL;
61 
62 FIRMWARE_PERFORMANCE_TABLE  mFirmwarePerformanceTableTemplate = {
63   {
64     EFI_ACPI_5_0_FIRMWARE_PERFORMANCE_DATA_TABLE_SIGNATURE,
65     sizeof (FIRMWARE_PERFORMANCE_TABLE),
66     EFI_ACPI_5_0_FIRMWARE_PERFORMANCE_DATA_TABLE_REVISION,    // Revision
67     0x00, // Checksum will be updated at runtime
68     //
69     // It is expected that these values will be updated at EntryPoint.
70     //
71     {0x00},     // OEM ID is a 6 bytes long field
72     0x00,       // OEM Table ID(8 bytes long)
73     0x00,       // OEM Revision
74     0x00,       // Creator ID
75     0x00,       // Creator Revision
76   },
77   //
78   // Firmware Basic Boot Performance Table Pointer Record.
79   //
80   {
81     {
82       EFI_ACPI_5_0_FPDT_RECORD_TYPE_FIRMWARE_BASIC_BOOT_POINTER ,       // Type
83       sizeof (EFI_ACPI_5_0_FPDT_BOOT_PERFORMANCE_TABLE_POINTER_RECORD), // Length
84       EFI_ACPI_5_0_FPDT_RECORD_REVISION_FIRMWARE_BASIC_BOOT_POINTER     // Revision
85     },
86     0,  // Reserved
87     0   // BootPerformanceTablePointer will be updated at runtime.
88   },
89   //
90   // S3 Performance Table Pointer Record.
91   //
92   {
93     {
94       EFI_ACPI_5_0_FPDT_RECORD_TYPE_S3_PERFORMANCE_TABLE_POINTER,     // Type
95       sizeof (EFI_ACPI_5_0_FPDT_S3_PERFORMANCE_TABLE_POINTER_RECORD), // Length
96       EFI_ACPI_5_0_FPDT_RECORD_REVISION_S3_PERFORMANCE_TABLE_POINTER  // Revision
97     },
98     0,  // Reserved
99     0   // S3PerformanceTablePointer will be updated at runtime.
100   }
101 };
102 
103 BOOT_PERFORMANCE_TABLE mBootPerformanceTableTemplate = {
104   {
105     EFI_ACPI_5_0_FPDT_BOOT_PERFORMANCE_TABLE_SIGNATURE,
106     sizeof (BOOT_PERFORMANCE_TABLE)
107   },
108   {
109     {
110       EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_TYPE_FIRMWARE_BASIC_BOOT,    // Type
111       sizeof (EFI_ACPI_5_0_FPDT_FIRMWARE_BASIC_BOOT_RECORD),        // Length
112       EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_REVISION_FIRMWARE_BASIC_BOOT // Revision
113     },
114     0,  // Reserved
115     //
116     // These values will be updated at runtime.
117     //
118     0,  // ResetEnd
119     0,  // OsLoaderLoadImageStart
120     0,  // OsLoaderStartImageStart
121     0,  // ExitBootServicesEntry
122     0   // ExitBootServicesExit
123   }
124 };
125 
126 S3_PERFORMANCE_TABLE        mS3PerformanceTableTemplate = {
127   {
128     EFI_ACPI_5_0_FPDT_S3_PERFORMANCE_TABLE_SIGNATURE,
129     sizeof (S3_PERFORMANCE_TABLE)
130   },
131   {
132     {
133       EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_TYPE_S3_RESUME,     // Type
134       sizeof (EFI_ACPI_5_0_FPDT_S3_RESUME_RECORD),         // Length
135       EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_REVISION_S3_RESUME  // Revision
136     },
137     //
138     // These values will be updated by Firmware Performance PEIM.
139     //
140     0,  // ResumeCount
141     0,  // FullResume
142     0   // AverageResume
143   },
144   {
145     {
146       EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_TYPE_S3_SUSPEND,    // Type
147       sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD),        // Length
148       EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_REVISION_S3_SUSPEND // Revision
149     },
150     //
151     // These values will be updated bye Firmware Performance SMM driver.
152     //
153     0,  // SuspendStart
154     0   // SuspendEnd
155   }
156 };
157 
158 /**
159   This function calculates and updates an UINT8 checksum.
160 
161   @param[in]  Buffer          Pointer to buffer to checksum
162   @param[in]  Size            Number of bytes to checksum
163 
164 **/
165 VOID
FpdtAcpiTableChecksum(IN UINT8 * Buffer,IN UINTN Size)166 FpdtAcpiTableChecksum (
167   IN UINT8      *Buffer,
168   IN UINTN      Size
169   )
170 {
171   UINTN ChecksumOffset;
172 
173   ChecksumOffset = OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, Checksum);
174 
175   //
176   // Set checksum to 0 first.
177   //
178   Buffer[ChecksumOffset] = 0;
179 
180   //
181   // Update checksum value.
182   //
183   Buffer[ChecksumOffset] = CalculateCheckSum8 (Buffer, Size);
184 }
185 
186 /**
187   Allocate EfiReservedMemoryType below 4G memory address.
188 
189   This function allocates EfiReservedMemoryType below 4G memory address.
190 
191   @param[in]  Size   Size of memory to allocate.
192 
193   @return Allocated address for output.
194 
195 **/
196 VOID *
FpdtAllocateReservedMemoryBelow4G(IN UINTN Size)197 FpdtAllocateReservedMemoryBelow4G (
198   IN UINTN       Size
199   )
200 {
201   UINTN                 Pages;
202   EFI_PHYSICAL_ADDRESS  Address;
203   EFI_STATUS            Status;
204   VOID                  *Buffer;
205 
206   Buffer  = NULL;
207   Pages   = EFI_SIZE_TO_PAGES (Size);
208   Address = 0xffffffff;
209 
210   Status = gBS->AllocatePages (
211                   AllocateMaxAddress,
212                   EfiReservedMemoryType,
213                   Pages,
214                   &Address
215                   );
216   ASSERT_EFI_ERROR (Status);
217 
218   if (!EFI_ERROR (Status)) {
219     Buffer = (VOID *) (UINTN) Address;
220     ZeroMem (Buffer, Size);
221   }
222 
223   return Buffer;
224 }
225 
226 /**
227   Callback function upon VariableArchProtocol and LockBoxProtocol
228   to allocate S3 performance table memory and save the pointer to LockBox.
229 
230   @param[in] Event    Event whose notification function is being invoked.
231   @param[in] Context  Pointer to the notification function's context.
232 **/
233 VOID
234 EFIAPI
FpdtAllocateS3PerformanceTableMemory(IN EFI_EVENT Event,IN VOID * Context)235 FpdtAllocateS3PerformanceTableMemory (
236   IN  EFI_EVENT                             Event,
237   IN  VOID                                  *Context
238   )
239 {
240   EFI_STATUS                    Status;
241   VOID                          *Interface;
242   FIRMWARE_PERFORMANCE_VARIABLE PerformanceVariable;
243   UINTN                         Size;
244   EFI_PHYSICAL_ADDRESS          S3PerformanceTablePointer;
245 
246   if (mLockBoxReady && (mAcpiS3PerformanceTable != NULL)) {
247     //
248     // The memory for S3 performance table should have been ready,
249     // and the pointer should have been saved to LockBox, just return.
250     //
251     return;
252   }
253 
254   if (!mLockBoxReady) {
255     Status = gBS->LocateProtocol (&gEfiLockBoxProtocolGuid, NULL, &Interface);
256     if (!EFI_ERROR (Status)) {
257       //
258       // LockBox services has been ready.
259       //
260       mLockBoxReady = TRUE;
261     }
262   }
263 
264   if (mAcpiS3PerformanceTable == NULL) {
265     Status = gBS->LocateProtocol (&gEfiVariableArchProtocolGuid, NULL, &Interface);
266     if (!EFI_ERROR (Status)) {
267       //
268       // Try to allocate the same runtime buffer as last time boot.
269       //
270       ZeroMem (&PerformanceVariable, sizeof (PerformanceVariable));
271       Size = sizeof (PerformanceVariable);
272       Status = gRT->GetVariable (
273                       EFI_FIRMWARE_PERFORMANCE_VARIABLE_NAME,
274                       &gEfiFirmwarePerformanceGuid,
275                       NULL,
276                       &Size,
277                       &PerformanceVariable
278                       );
279       if (!EFI_ERROR (Status)) {
280         Status = gBS->AllocatePages (
281                         AllocateAddress,
282                         EfiReservedMemoryType,
283                         EFI_SIZE_TO_PAGES (sizeof (S3_PERFORMANCE_TABLE)),
284                         &PerformanceVariable.S3PerformanceTablePointer
285                         );
286         if (!EFI_ERROR (Status)) {
287           mAcpiS3PerformanceTable = (S3_PERFORMANCE_TABLE *) (UINTN) PerformanceVariable.S3PerformanceTablePointer;
288         }
289       }
290       if (mAcpiS3PerformanceTable == NULL) {
291         //
292         // Fail to allocate at specified address, continue to allocate at any address.
293         //
294         mAcpiS3PerformanceTable = (S3_PERFORMANCE_TABLE *) FpdtAllocateReservedMemoryBelow4G (sizeof (S3_PERFORMANCE_TABLE));
295       }
296       DEBUG ((EFI_D_INFO, "FPDT: ACPI S3 Performance Table address = 0x%x\n", mAcpiS3PerformanceTable));
297       if (mAcpiS3PerformanceTable != NULL) {
298         CopyMem (mAcpiS3PerformanceTable, &mS3PerformanceTableTemplate, sizeof (mS3PerformanceTableTemplate));
299       }
300     }
301   }
302 
303   if (mLockBoxReady && (mAcpiS3PerformanceTable != NULL)) {
304     //
305     // If LockBox services has been ready and memory for FPDT S3 performance table has been allocated,
306     // save the pointer to LockBox for use in S3 resume.
307     //
308     S3PerformanceTablePointer = (EFI_PHYSICAL_ADDRESS) (UINTN) mAcpiS3PerformanceTable;
309     Status = SaveLockBox (
310                &gFirmwarePerformanceS3PointerGuid,
311                &S3PerformanceTablePointer,
312                sizeof (EFI_PHYSICAL_ADDRESS)
313                );
314     ASSERT_EFI_ERROR (Status);
315   }
316 }
317 
318 /**
319   Install ACPI Firmware Performance Data Table (FPDT).
320 
321   @return Status code.
322 
323 **/
324 EFI_STATUS
InstallFirmwarePerformanceDataTable(VOID)325 InstallFirmwarePerformanceDataTable (
326   VOID
327   )
328 {
329   EFI_STATUS                    Status;
330   EFI_ACPI_TABLE_PROTOCOL       *AcpiTableProtocol;
331   UINTN                         Size;
332   UINT8                         *SmmBootRecordCommBuffer;
333   EFI_SMM_COMMUNICATE_HEADER    *SmmCommBufferHeader;
334   SMM_BOOT_RECORD_COMMUNICATE   *SmmCommData;
335   UINTN                         CommSize;
336   UINTN                         BootPerformanceDataSize;
337   UINT8                         *BootPerformanceData;
338   EFI_SMM_COMMUNICATION_PROTOCOL  *Communication;
339   FIRMWARE_PERFORMANCE_VARIABLE PerformanceVariable;
340 
341   //
342   // Get AcpiTable Protocol.
343   //
344   Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol);
345   if (EFI_ERROR (Status)) {
346     return Status;
347   }
348 
349   //
350   // Collect boot records from SMM drivers.
351   //
352   SmmBootRecordCommBuffer = NULL;
353   SmmCommData             = NULL;
354   Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &Communication);
355   if (!EFI_ERROR (Status)) {
356     //
357     // Initialize communicate buffer
358     //
359     SmmBootRecordCommBuffer = AllocateZeroPool (SMM_BOOT_RECORD_COMM_SIZE);
360     ASSERT (SmmBootRecordCommBuffer != NULL);
361     SmmCommBufferHeader = (EFI_SMM_COMMUNICATE_HEADER*)SmmBootRecordCommBuffer;
362     SmmCommData = (SMM_BOOT_RECORD_COMMUNICATE*)SmmCommBufferHeader->Data;
363     ZeroMem((UINT8*)SmmCommData, sizeof(SMM_BOOT_RECORD_COMMUNICATE));
364 
365     CopyGuid (&SmmCommBufferHeader->HeaderGuid, &gEfiFirmwarePerformanceGuid);
366     SmmCommBufferHeader->MessageLength = sizeof(SMM_BOOT_RECORD_COMMUNICATE);
367     CommSize = SMM_BOOT_RECORD_COMM_SIZE;
368 
369     //
370     // Get the size of boot records.
371     //
372     SmmCommData->Function       = SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE;
373     SmmCommData->BootRecordData = NULL;
374     Status = Communication->Communicate (Communication, SmmBootRecordCommBuffer, &CommSize);
375     ASSERT_EFI_ERROR (Status);
376 
377     if (!EFI_ERROR (SmmCommData->ReturnStatus) && SmmCommData->BootRecordSize != 0) {
378       //
379       // Get all boot records
380       //
381       SmmCommData->Function       = SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA;
382       SmmCommData->BootRecordData = AllocateZeroPool(SmmCommData->BootRecordSize);
383       ASSERT (SmmCommData->BootRecordData != NULL);
384 
385       Status = Communication->Communicate (Communication, SmmBootRecordCommBuffer, &CommSize);
386       ASSERT_EFI_ERROR (Status);
387       ASSERT_EFI_ERROR(SmmCommData->ReturnStatus);
388     }
389   }
390 
391   //
392   // Prepare memory for Boot Performance table.
393   // Boot Performance table includes BasicBoot record, and one or more appended Boot Records.
394   //
395   BootPerformanceDataSize = sizeof (BOOT_PERFORMANCE_TABLE) + mBootRecordSize + PcdGet32 (PcdExtFpdtBootRecordPadSize);
396   if (SmmCommData != NULL) {
397     BootPerformanceDataSize += SmmCommData->BootRecordSize;
398   }
399 
400   //
401   // Try to allocate the same runtime buffer as last time boot.
402   //
403   ZeroMem (&PerformanceVariable, sizeof (PerformanceVariable));
404   Size = sizeof (PerformanceVariable);
405   Status = gRT->GetVariable (
406                   EFI_FIRMWARE_PERFORMANCE_VARIABLE_NAME,
407                   &gEfiFirmwarePerformanceGuid,
408                   NULL,
409                   &Size,
410                   &PerformanceVariable
411                   );
412   if (!EFI_ERROR (Status)) {
413     Status = gBS->AllocatePages (
414                     AllocateAddress,
415                     EfiReservedMemoryType,
416                     EFI_SIZE_TO_PAGES (BootPerformanceDataSize),
417                     &PerformanceVariable.BootPerformanceTablePointer
418                     );
419     if (!EFI_ERROR (Status)) {
420       mAcpiBootPerformanceTable = (BOOT_PERFORMANCE_TABLE *) (UINTN) PerformanceVariable.BootPerformanceTablePointer;
421     }
422   }
423 
424   if (mAcpiBootPerformanceTable == NULL) {
425     //
426     // Fail to allocate at specified address, continue to allocate at any address.
427     //
428     mAcpiBootPerformanceTable = (BOOT_PERFORMANCE_TABLE *) FpdtAllocateReservedMemoryBelow4G (BootPerformanceDataSize);
429   }
430   DEBUG ((EFI_D_INFO, "FPDT: ACPI Boot Performance Table address = 0x%x\n", mAcpiBootPerformanceTable));
431 
432   if (mAcpiBootPerformanceTable == NULL) {
433     if (SmmCommData != NULL && SmmCommData->BootRecordData != NULL) {
434       FreePool (SmmCommData->BootRecordData);
435     }
436     if (SmmBootRecordCommBuffer != NULL) {
437       FreePool (SmmBootRecordCommBuffer);
438     }
439     if (mAcpiS3PerformanceTable != NULL) {
440       FreePages (mAcpiS3PerformanceTable, EFI_SIZE_TO_PAGES (sizeof (S3_PERFORMANCE_TABLE)));
441     }
442     return EFI_OUT_OF_RESOURCES;
443   }
444 
445   //
446   // Prepare Boot Performance Table.
447   //
448   BootPerformanceData = (UINT8 *) mAcpiBootPerformanceTable;
449   //
450   // Fill Basic Boot record to Boot Performance Table.
451   //
452   CopyMem (mAcpiBootPerformanceTable, &mBootPerformanceTableTemplate, sizeof (mBootPerformanceTableTemplate));
453   BootPerformanceData = BootPerformanceData + mAcpiBootPerformanceTable->Header.Length;
454   //
455   // Fill Boot records from boot drivers.
456   //
457   CopyMem (BootPerformanceData, mBootRecordBuffer, mBootRecordSize);
458   mAcpiBootPerformanceTable->Header.Length += mBootRecordSize;
459   BootPerformanceData = BootPerformanceData + mBootRecordSize;
460   if (SmmCommData != NULL && SmmCommData->BootRecordData != NULL) {
461     //
462     // Fill Boot records from SMM drivers.
463     //
464     CopyMem (BootPerformanceData, SmmCommData->BootRecordData, SmmCommData->BootRecordSize);
465     FreePool (SmmCommData->BootRecordData);
466     mAcpiBootPerformanceTable->Header.Length = (UINT32) (mAcpiBootPerformanceTable->Header.Length + SmmCommData->BootRecordSize);
467     BootPerformanceData = BootPerformanceData + SmmCommData->BootRecordSize;
468   }
469   if (SmmBootRecordCommBuffer != NULL) {
470     FreePool (SmmBootRecordCommBuffer);
471   }
472 
473   //
474   // Save Boot Performance Table address to Variable for use in S4 resume.
475   //
476   PerformanceVariable.BootPerformanceTablePointer = (EFI_PHYSICAL_ADDRESS) (UINTN) mAcpiBootPerformanceTable;
477   //
478   // Update Boot Performance Table Pointer in template.
479   //
480   mFirmwarePerformanceTableTemplate.BootPointerRecord.BootPerformanceTablePointer = (UINT64) (UINTN) mAcpiBootPerformanceTable;
481 
482   //
483   // Save S3 Performance Table address to Variable for use in S4 resume.
484   //
485   PerformanceVariable.S3PerformanceTablePointer = (EFI_PHYSICAL_ADDRESS) (UINTN) mAcpiS3PerformanceTable;
486   //
487   // Update S3 Performance Table Pointer in template.
488   //
489   mFirmwarePerformanceTableTemplate.S3PointerRecord.S3PerformanceTablePointer = (UINT64) (UINTN) mAcpiS3PerformanceTable;
490   //
491   // Save Runtime Performance Table pointers to Variable.
492   // Don't check SetVariable return status. It doesn't impact FPDT table generation.
493   //
494   gRT->SetVariable (
495         EFI_FIRMWARE_PERFORMANCE_VARIABLE_NAME,
496         &gEfiFirmwarePerformanceGuid,
497         EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
498         sizeof (PerformanceVariable),
499         &PerformanceVariable
500         );
501 
502   //
503   // Publish Firmware Performance Data Table.
504   //
505   FpdtAcpiTableChecksum ((UINT8 *) &mFirmwarePerformanceTableTemplate, mFirmwarePerformanceTableTemplate.Header.Length);
506   Status = AcpiTableProtocol->InstallAcpiTable (
507                                 AcpiTableProtocol,
508                                 &mFirmwarePerformanceTableTemplate,
509                                 mFirmwarePerformanceTableTemplate.Header.Length,
510                                 &mFirmwarePerformanceTableTemplateKey
511                                 );
512   if (EFI_ERROR (Status)) {
513     FreePages (mAcpiBootPerformanceTable, EFI_SIZE_TO_PAGES (BootPerformanceDataSize));
514     if (mAcpiS3PerformanceTable != NULL) {
515       FreePages (mAcpiS3PerformanceTable, EFI_SIZE_TO_PAGES (sizeof (S3_PERFORMANCE_TABLE)));
516     }
517     mAcpiBootPerformanceTable = NULL;
518     mAcpiS3PerformanceTable = NULL;
519     return Status;
520   }
521 
522   //
523   // Free temp Boot record, and update Boot Record to point to Basic Boot performance table.
524   //
525   if (mBootRecordBuffer != NULL) {
526     FreePool (mBootRecordBuffer);
527   }
528   mBootRecordBuffer  = (UINT8 *) mAcpiBootPerformanceTable;
529   mBootRecordSize    = mAcpiBootPerformanceTable->Header.Length;
530   mBootRecordMaxSize = mBootRecordSize + PcdGet32 (PcdExtFpdtBootRecordPadSize);
531 
532   return EFI_SUCCESS;
533 }
534 
535 /**
536   Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to
537   install the Firmware Performance Data Table.
538 
539   @param[in]  Event   The Event that is being processed.
540   @param[in]  Context The Event Context.
541 
542 **/
543 VOID
544 EFIAPI
FpdtReadyToBootEventNotify(IN EFI_EVENT Event,IN VOID * Context)545 FpdtReadyToBootEventNotify (
546   IN EFI_EVENT        Event,
547   IN VOID             *Context
548   )
549 {
550   if (mAcpiBootPerformanceTable == NULL) {
551     //
552     // ACPI Firmware Performance Data Table not installed yet, install it now.
553     //
554     InstallFirmwarePerformanceDataTable ();
555   }
556 }
557 
558 /**
559   Report status code listener of FPDT. This is used to collect performance data
560   for OsLoaderLoadImageStart and OsLoaderStartImageStart in FPDT.
561 
562   @param[in]  CodeType            Indicates the type of status code being reported.
563   @param[in]  Value               Describes the current status of a hardware or software entity.
564                                   This included information about the class and subclass that is used to
565                                   classify the entity as well as an operation.
566   @param[in]  Instance            The enumeration of a hardware or software entity within
567                                   the system. Valid instance numbers start with 1.
568   @param[in]  CallerId            This optional parameter may be used to identify the caller.
569                                   This parameter allows the status code driver to apply different rules to
570                                   different callers.
571   @param[in]  Data                This optional parameter may be used to pass additional data.
572 
573   @retval EFI_SUCCESS             Status code is what we expected.
574   @retval EFI_UNSUPPORTED         Status code not supported.
575 
576 **/
577 EFI_STATUS
578 EFIAPI
FpdtStatusCodeListenerDxe(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)579 FpdtStatusCodeListenerDxe (
580   IN EFI_STATUS_CODE_TYPE     CodeType,
581   IN EFI_STATUS_CODE_VALUE    Value,
582   IN UINT32                   Instance,
583   IN EFI_GUID                 *CallerId,
584   IN EFI_STATUS_CODE_DATA     *Data
585   )
586 {
587   EFI_STATUS  Status;
588 
589   //
590   // Check whether status code is what we are interested in.
591   //
592   if ((CodeType & EFI_STATUS_CODE_TYPE_MASK) != EFI_PROGRESS_CODE) {
593     return EFI_UNSUPPORTED;
594   }
595 
596   if (Value == (EFI_SOFTWARE_DXE_CORE | EFI_SW_DXE_CORE_PC_HANDOFF_TO_NEXT)) {
597     //
598     // DxeCore ReportStatusCode Enable so that the capability can be supported.
599     //
600     mDxeCoreReportStatusCodeEnable = TRUE;
601   }
602 
603   Status = EFI_SUCCESS;
604   if (Value == PcdGet32 (PcdProgressCodeOsLoaderLoad)) {
605     //
606     // Progress code for OS Loader LoadImage.
607     //
608     if (mAcpiBootPerformanceTable == NULL) {
609       return Status;
610     }
611 
612     //
613     // Update OS Loader LoadImage Start for UEFI boot.
614     //
615     mAcpiBootPerformanceTable->BasicBoot.OsLoaderLoadImageStart = GetTimeInNanoSecond (GetPerformanceCounter ());
616   } else if (Value == PcdGet32 (PcdProgressCodeOsLoaderStart)) {
617     //
618     // Progress code for OS Loader StartImage.
619     //
620     if (mAcpiBootPerformanceTable == NULL) {
621       return Status;
622     }
623 
624     //
625     // Update OS Loader StartImage Start for UEFI boot.
626     //
627     mAcpiBootPerformanceTable->BasicBoot.OsLoaderStartImageStart = GetTimeInNanoSecond (GetPerformanceCounter ());
628   } else if (Value == (EFI_SOFTWARE_EFI_BOOT_SERVICE | EFI_SW_BS_PC_EXIT_BOOT_SERVICES)) {
629     //
630     // Unregister boot time report status code listener.
631     //
632     mRscHandlerProtocol->Unregister (FpdtStatusCodeListenerDxe);
633 
634     //
635     // Progress code for ExitBootServices.
636     //
637     if (mAcpiBootPerformanceTable == NULL) {
638       return Status;
639     }
640 
641     //
642     // Update ExitBootServicesExit for UEFI boot.
643     //
644     mAcpiBootPerformanceTable->BasicBoot.ExitBootServicesExit = GetTimeInNanoSecond (GetPerformanceCounter ());
645   } else if (Value == (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_LEGACY_BOOT_EVENT)) {
646     if (mAcpiBootPerformanceTable == NULL) {
647       //
648       // Firmware Performance Data Table not installed, do nothing.
649       //
650       return Status;
651     }
652 
653     //
654     // Update Firmware Basic Boot Performance Record for legacy boot.
655     //
656     mAcpiBootPerformanceTable->BasicBoot.OsLoaderStartImageStart = GetTimeInNanoSecond (GetPerformanceCounter ());
657 
658     //
659     // Dump FPDT Boot Performance record.
660     //
661     DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ResetEnd                = %ld\n", mAcpiBootPerformanceTable->BasicBoot.ResetEnd));
662     DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - OsLoaderLoadImageStart  = 0\n"));
663     DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - OsLoaderStartImageStart = %ld\n", mAcpiBootPerformanceTable->BasicBoot.OsLoaderStartImageStart));
664     DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ExitBootServicesEntry   = 0\n"));
665     DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ExitBootServicesExit    = 0\n"));
666   } else if (Data != NULL && CompareGuid (&Data->Type, &gEfiFirmwarePerformanceGuid)) {
667     //
668     // Append one or more Boot records
669     //
670     if (mAcpiBootPerformanceTable == NULL) {
671       //
672       // Append Boot records before FPDT ACPI table is installed.
673       //
674       if (mBootRecordSize + Data->Size > mBootRecordMaxSize) {
675         mBootRecordBuffer = ReallocatePool (mBootRecordSize, mBootRecordSize + Data->Size + EXTENSION_RECORD_SIZE, mBootRecordBuffer);
676         ASSERT (mBootRecordBuffer != NULL);
677         mBootRecordMaxSize = mBootRecordSize + Data->Size + EXTENSION_RECORD_SIZE;
678       }
679       //
680       // Save boot record into the temp memory space.
681       //
682       CopyMem (mBootRecordBuffer + mBootRecordSize, Data + 1, Data->Size);
683       mBootRecordSize += Data->Size;
684     } else {
685       //
686       // Append Boot records after FPDT ACPI table is installed.
687       //
688       if (mBootRecordSize + Data->Size > mBootRecordMaxSize) {
689         //
690         // No enough space to save boot record.
691         //
692         Status = EFI_OUT_OF_RESOURCES;
693       } else {
694         //
695         // Save boot record into BootPerformance table
696         //
697         CopyMem (mBootRecordBuffer + mBootRecordSize, Data + 1, Data->Size);
698         mBootRecordSize += Data->Size;
699         mAcpiBootPerformanceTable->Header.Length = mBootRecordSize;
700       }
701     }
702   } else {
703     //
704     // Ignore else progress code.
705     //
706     Status = EFI_UNSUPPORTED;
707   }
708 
709   return Status;
710 }
711 
712 
713 /**
714   Notify function for event EVT_SIGNAL_EXIT_BOOT_SERVICES. This is used to record
715   performance data for ExitBootServicesEntry in FPDT.
716 
717   @param[in]  Event   The Event that is being processed.
718   @param[in]  Context The Event Context.
719 
720 **/
721 VOID
722 EFIAPI
FpdtExitBootServicesEventNotify(IN EFI_EVENT Event,IN VOID * Context)723 FpdtExitBootServicesEventNotify (
724   IN EFI_EVENT        Event,
725   IN VOID             *Context
726   )
727 {
728   if (!mDxeCoreReportStatusCodeEnable) {
729     //
730     // When DxeCore Report Status Code is disabled,
731     // Unregister boot time report status code listener at ExitBootService Event.
732     //
733     mRscHandlerProtocol->Unregister (FpdtStatusCodeListenerDxe);
734   }
735 
736   if (mAcpiBootPerformanceTable == NULL) {
737     //
738     // Firmware Performance Data Table not installed, do nothing.
739     //
740     return ;
741   }
742 
743   //
744   // Update Firmware Basic Boot Performance Record for UEFI boot.
745   //
746   mAcpiBootPerformanceTable->BasicBoot.ExitBootServicesEntry = GetTimeInNanoSecond (GetPerformanceCounter ());
747 
748   //
749   // Dump FPDT Boot Performance record.
750   //
751   DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ResetEnd                = %ld\n", mAcpiBootPerformanceTable->BasicBoot.ResetEnd));
752   DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - OsLoaderLoadImageStart  = %ld\n", mAcpiBootPerformanceTable->BasicBoot.OsLoaderLoadImageStart));
753   DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - OsLoaderStartImageStart = %ld\n", mAcpiBootPerformanceTable->BasicBoot.OsLoaderStartImageStart));
754   DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ExitBootServicesEntry   = %ld\n", mAcpiBootPerformanceTable->BasicBoot.ExitBootServicesEntry));
755   //
756   // ExitBootServicesExit will be updated later, so don't dump it here.
757   //
758 }
759 
760 /**
761   The module Entry Point of the Firmware Performance Data Table DXE driver.
762 
763   @param[in]  ImageHandle    The firmware allocated handle for the EFI image.
764   @param[in]  SystemTable    A pointer to the EFI System Table.
765 
766   @retval EFI_SUCCESS    The entry point is executed successfully.
767   @retval Other          Some error occurs when executing this entry point.
768 
769 **/
770 EFI_STATUS
771 EFIAPI
FirmwarePerformanceDxeEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)772 FirmwarePerformanceDxeEntryPoint (
773   IN EFI_HANDLE          ImageHandle,
774   IN EFI_SYSTEM_TABLE    *SystemTable
775   )
776 {
777   EFI_STATUS               Status;
778   EFI_HOB_GUID_TYPE        *GuidHob;
779   FIRMWARE_SEC_PERFORMANCE *Performance;
780   VOID                     *Registration;
781   UINT64                   OemTableId;
782 
783   CopyMem (
784     mFirmwarePerformanceTableTemplate.Header.OemId,
785     PcdGetPtr (PcdAcpiDefaultOemId),
786     sizeof (mFirmwarePerformanceTableTemplate.Header.OemId)
787     );
788   OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId);
789   CopyMem (&mFirmwarePerformanceTableTemplate.Header.OemTableId, &OemTableId, sizeof (UINT64));
790   mFirmwarePerformanceTableTemplate.Header.OemRevision      = PcdGet32 (PcdAcpiDefaultOemRevision);
791   mFirmwarePerformanceTableTemplate.Header.CreatorId        = PcdGet32 (PcdAcpiDefaultCreatorId);
792   mFirmwarePerformanceTableTemplate.Header.CreatorRevision  = PcdGet32 (PcdAcpiDefaultCreatorRevision);
793 
794   //
795   // Get Report Status Code Handler Protocol.
796   //
797   Status = gBS->LocateProtocol (&gEfiRscHandlerProtocolGuid, NULL, (VOID **) &mRscHandlerProtocol);
798   ASSERT_EFI_ERROR (Status);
799 
800   //
801   // Register report status code listener for OS Loader load and start.
802   //
803   Status = mRscHandlerProtocol->Register (FpdtStatusCodeListenerDxe, TPL_HIGH_LEVEL);
804   ASSERT_EFI_ERROR (Status);
805 
806   //
807   // Register the notify function to update FPDT on ExitBootServices Event.
808   //
809   Status = gBS->CreateEventEx (
810                   EVT_NOTIFY_SIGNAL,
811                   TPL_NOTIFY,
812                   FpdtExitBootServicesEventNotify,
813                   NULL,
814                   &gEfiEventExitBootServicesGuid,
815                   &mExitBootServicesEvent
816                   );
817   ASSERT_EFI_ERROR (Status);
818 
819   //
820   // Create ready to boot event to install ACPI FPDT table.
821   //
822   Status = gBS->CreateEventEx (
823                   EVT_NOTIFY_SIGNAL,
824                   TPL_NOTIFY,
825                   FpdtReadyToBootEventNotify,
826                   NULL,
827                   &gEfiEventReadyToBootGuid,
828                   &mReadyToBootEvent
829                   );
830   ASSERT_EFI_ERROR (Status);
831 
832   //
833   // Retrieve GUID HOB data that contains the ResetEnd.
834   //
835   GuidHob = GetFirstGuidHob (&gEfiFirmwarePerformanceGuid);
836   if (GuidHob != NULL) {
837     Performance = (FIRMWARE_SEC_PERFORMANCE *) GET_GUID_HOB_DATA (GuidHob);
838     mBootPerformanceTableTemplate.BasicBoot.ResetEnd = Performance->ResetEnd;
839   } else {
840     //
841     // SEC Performance Data Hob not found, ResetEnd in ACPI FPDT table will be 0.
842     //
843     DEBUG ((EFI_D_ERROR, "FPDT: WARNING: SEC Performance Data Hob not found, ResetEnd will be set to 0!\n"));
844   }
845 
846   if (FeaturePcdGet (PcdFirmwarePerformanceDataTableS3Support)) {
847     //
848     // Register callback function upon VariableArchProtocol and LockBoxProtocol
849     // to allocate S3 performance table memory and save the pointer to LockBox.
850     //
851     EfiCreateProtocolNotifyEvent (
852       &gEfiVariableArchProtocolGuid,
853       TPL_CALLBACK,
854       FpdtAllocateS3PerformanceTableMemory,
855       NULL,
856       &Registration
857       );
858     EfiCreateProtocolNotifyEvent (
859       &gEfiLockBoxProtocolGuid,
860       TPL_CALLBACK,
861       FpdtAllocateS3PerformanceTableMemory,
862       NULL,
863       &Registration
864       );
865   } else {
866     //
867     // Exclude S3 Performance Table Pointer from FPDT table template.
868     //
869     mFirmwarePerformanceTableTemplate.Header.Length -= sizeof (EFI_ACPI_5_0_FPDT_S3_PERFORMANCE_TABLE_POINTER_RECORD);
870   }
871 
872   return EFI_SUCCESS;
873 }
874