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