1 /** @file
2   This module updates S3 Resume Performance Record in ACPI Firmware Performance
3   Data Table in S3 resume boot mode. In normal boot mode, this module consumes
4   SecPerformance PPI produced by SEC phase and build Hob to convey the SEC
5   performance data to DXE phase.
6 
7   This module register report status code listener to collect performance data
8   for S3 Resume Performance Record on S3 resume boot path.
9 
10   Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
11   This program and the accompanying materials
12   are licensed and made available under the terms and conditions of the BSD License
13   which accompanies this distribution.  The full text of the license may be found at
14   http://opensource.org/licenses/bsd-license.php
15 
16   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
17   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 
19 **/
20 
21 #include <PiPei.h>
22 
23 #include <Ppi/ReportStatusCodeHandler.h>
24 #include <Ppi/SecPerformance.h>
25 
26 #include <Guid/FirmwarePerformance.h>
27 
28 #include <Library/PeiServicesLib.h>
29 #include <Library/BaseLib.h>
30 #include <Library/DebugLib.h>
31 #include <Library/TimerLib.h>
32 #include <Library/BaseMemoryLib.h>
33 #include <Library/LockBoxLib.h>
34 #include <Library/HobLib.h>
35 #include <Library/PcdLib.h>
36 
37 /**
38   Report status code listener for PEI. This is used to record the performance
39   data for S3 FullResume in FPDT.
40 
41   @param[in]  PeiServices         An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
42   @param[in]  CodeType            Indicates the type of status code being reported.
43   @param[in]  Value               Describes the current status of a hardware or software entity.
44                                   This included information about the class and subclass that is used to
45                                   classify the entity as well as an operation.
46   @param[in]  Instance            The enumeration of a hardware or software entity within
47                                   the system. Valid instance numbers start with 1.
48   @param[in]  CallerId            This optional parameter may be used to identify the caller.
49                                   This parameter allows the status code driver to apply different rules to
50                                   different callers.
51   @param[in]  Data                This optional parameter may be used to pass additional data.
52 
53   @retval EFI_SUCCESS             Status code is what we expected.
54   @retval EFI_UNSUPPORTED         Status code not supported.
55 
56 **/
57 EFI_STATUS
58 EFIAPI
FpdtStatusCodeListenerPei(IN CONST EFI_PEI_SERVICES ** PeiServices,IN EFI_STATUS_CODE_TYPE CodeType,IN EFI_STATUS_CODE_VALUE Value,IN UINT32 Instance,IN CONST EFI_GUID * CallerId,IN CONST EFI_STATUS_CODE_DATA * Data)59 FpdtStatusCodeListenerPei (
60   IN CONST  EFI_PEI_SERVICES        **PeiServices,
61   IN        EFI_STATUS_CODE_TYPE    CodeType,
62   IN        EFI_STATUS_CODE_VALUE   Value,
63   IN        UINT32                  Instance,
64   IN CONST  EFI_GUID                *CallerId,
65   IN CONST  EFI_STATUS_CODE_DATA    *Data
66   )
67 {
68   EFI_STATUS                           Status;
69   UINT64                               CurrentTime;
70   UINTN                                VarSize;
71   EFI_PHYSICAL_ADDRESS                 S3PerformanceTablePointer;
72   S3_PERFORMANCE_TABLE                 *AcpiS3PerformanceTable;
73   EFI_ACPI_5_0_FPDT_S3_RESUME_RECORD   *AcpiS3ResumeRecord;
74   UINT64                               S3ResumeTotal;
75   EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD  S3SuspendRecord;
76   EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD  *AcpiS3SuspendRecord;
77 
78   //
79   // Check whether status code is what we are interested in.
80   //
81   if (((CodeType & EFI_STATUS_CODE_TYPE_MASK) != EFI_PROGRESS_CODE) ||
82   	  (Value != (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_PC_OS_WAKE))) {
83     return EFI_UNSUPPORTED;
84   }
85 
86   //
87   // Retrieve current time as early as possible.
88   //
89   CurrentTime = GetTimeInNanoSecond (GetPerformanceCounter ());
90 
91   //
92   // Update S3 Resume Performance Record.
93   //
94   S3PerformanceTablePointer = 0;
95   VarSize = sizeof (EFI_PHYSICAL_ADDRESS);
96   Status = RestoreLockBox (&gFirmwarePerformanceS3PointerGuid, &S3PerformanceTablePointer, &VarSize);
97   ASSERT_EFI_ERROR (Status);
98 
99   AcpiS3PerformanceTable = (S3_PERFORMANCE_TABLE *) (UINTN) S3PerformanceTablePointer;
100   ASSERT (AcpiS3PerformanceTable != NULL);
101   if (AcpiS3PerformanceTable->Header.Signature != EFI_ACPI_5_0_FPDT_S3_PERFORMANCE_TABLE_SIGNATURE) {
102     DEBUG ((EFI_D_ERROR, "FPDT S3 performance data in ACPI memory get corrupted\n"));
103     return EFI_ABORTED;
104   }
105   AcpiS3ResumeRecord = &AcpiS3PerformanceTable->S3Resume;
106   AcpiS3ResumeRecord->FullResume = CurrentTime;
107   //
108   // Calculate average S3 resume time.
109   //
110   S3ResumeTotal = MultU64x32 (AcpiS3ResumeRecord->AverageResume, AcpiS3ResumeRecord->ResumeCount);
111   AcpiS3ResumeRecord->ResumeCount++;
112   AcpiS3ResumeRecord->AverageResume = DivU64x32 (S3ResumeTotal + AcpiS3ResumeRecord->FullResume, AcpiS3ResumeRecord->ResumeCount);
113 
114   DEBUG ((EFI_D_INFO, "FPDT: S3 Resume Performance - ResumeCount   = %d\n", AcpiS3ResumeRecord->ResumeCount));
115   DEBUG ((EFI_D_INFO, "FPDT: S3 Resume Performance - FullResume    = %ld\n", AcpiS3ResumeRecord->FullResume));
116   DEBUG ((EFI_D_INFO, "FPDT: S3 Resume Performance - AverageResume = %ld\n", AcpiS3ResumeRecord->AverageResume));
117 
118   //
119   // Update S3 Suspend Performance Record.
120   //
121   AcpiS3SuspendRecord = &AcpiS3PerformanceTable->S3Suspend;
122   VarSize = sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD);
123   ZeroMem (&S3SuspendRecord, sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD));
124   Status = RestoreLockBox (
125              &gEfiFirmwarePerformanceGuid,
126              &S3SuspendRecord,
127              &VarSize
128              );
129   ASSERT_EFI_ERROR (Status);
130 
131   AcpiS3SuspendRecord->SuspendStart = S3SuspendRecord.SuspendStart;
132   AcpiS3SuspendRecord->SuspendEnd   = S3SuspendRecord.SuspendEnd;
133 
134   DEBUG ((EFI_D_INFO, "FPDT: S3 Suspend Performance - SuspendStart = %ld\n", AcpiS3SuspendRecord->SuspendStart));
135   DEBUG ((EFI_D_INFO, "FPDT: S3 Suspend Performance - SuspendEnd   = %ld\n", AcpiS3SuspendRecord->SuspendEnd));
136 
137   return EFI_SUCCESS;
138 }
139 
140 /**
141   Main entry for Firmware Performance Data Table PEIM.
142 
143   This routine is to register report status code listener for FPDT.
144 
145   @param[in]  FileHandle              Handle of the file being invoked.
146   @param[in]  PeiServices             Pointer to PEI Services table.
147 
148   @retval EFI_SUCCESS Report status code listener is registered successfully.
149 
150 **/
151 EFI_STATUS
152 EFIAPI
FirmwarePerformancePeiEntryPoint(IN EFI_PEI_FILE_HANDLE FileHandle,IN CONST EFI_PEI_SERVICES ** PeiServices)153 FirmwarePerformancePeiEntryPoint (
154   IN       EFI_PEI_FILE_HANDLE  FileHandle,
155   IN CONST EFI_PEI_SERVICES     **PeiServices
156   )
157 {
158   EFI_STATUS               Status;
159   EFI_PEI_RSC_HANDLER_PPI  *RscHandler;
160   PEI_SEC_PERFORMANCE_PPI  *SecPerf;
161   FIRMWARE_SEC_PERFORMANCE Performance;
162 
163   if (FeaturePcdGet (PcdFirmwarePerformanceDataTableS3Support)) {
164     //
165     // S3 resume - register status code listener for OS wake vector.
166     //
167     Status = PeiServicesLocatePpi (
168                &gEfiPeiRscHandlerPpiGuid,
169                0,
170                NULL,
171                (VOID **) &RscHandler
172                );
173     ASSERT_EFI_ERROR (Status);
174 
175     Status = RscHandler->Register (FpdtStatusCodeListenerPei);
176     ASSERT_EFI_ERROR (Status);
177   }
178 
179   //
180   // Normal boot - build Hob for SEC performance data.
181   //
182   Status = PeiServicesLocatePpi (
183              &gPeiSecPerformancePpiGuid,
184              0,
185              NULL,
186              (VOID **) &SecPerf
187              );
188   if (!EFI_ERROR (Status)) {
189     Status = SecPerf->GetPerformance (PeiServices, SecPerf, &Performance);
190   }
191   if (!EFI_ERROR (Status)) {
192     BuildGuidDataHob (
193       &gEfiFirmwarePerformanceGuid,
194       &Performance,
195       sizeof (FIRMWARE_SEC_PERFORMANCE)
196     );
197     DEBUG ((EFI_D_INFO, "FPDT: SEC Performance Hob ResetEnd = %ld\n", Performance.ResetEnd));
198   } else {
199     //
200     // SEC performance PPI is not installed or fail to get performance data
201     // from SEC Performance PPI.
202     //
203     DEBUG ((EFI_D_ERROR, "FPDT: WARNING: SEC Performance PPI not installed or failed!\n"));
204   }
205 
206   return EFI_SUCCESS;
207 }
208