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