1 /** @file
2   Report Status Code Router PEIM which produces Report Stataus Code Handler PPI and Status Code PPI.
3 
4   Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "ReportStatusCodeRouterPei.h"
10 
11 EFI_PEI_RSC_HANDLER_PPI     mRscHandlerPpi = {
12   Register,
13   Unregister
14   };
15 
16 EFI_PEI_PROGRESS_CODE_PPI     mStatusCodePpi = {
17   ReportDispatcher
18   };
19 
20 EFI_PEI_PPI_DESCRIPTOR   mRscHandlerPpiList[] = {
21   {
22     EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
23     &gEfiPeiRscHandlerPpiGuid,
24     &mRscHandlerPpi
25   }
26 };
27 
28 EFI_PEI_PPI_DESCRIPTOR   mStatusCodePpiList[] = {
29   {
30     EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
31     &gEfiPeiStatusCodePpiGuid,
32     &mStatusCodePpi
33   }
34 };
35 
36 /**
37   Worker function to create one memory status code GUID'ed HOB,
38   using PacketIndex to identify the packet.
39 
40   @param   PacketIndex    Index of records packet.
41 
42   @return  Pointer to the memory status code packet.
43 
44 **/
45 UINTN *
CreateRscHandlerCallbackPacket(VOID)46 CreateRscHandlerCallbackPacket (
47   VOID
48   )
49 {
50   UINTN  *NumberOfEntries;
51 
52   //
53   // Build GUID'ed HOB with PCD defined size.
54   //
55   NumberOfEntries = BuildGuidHob (
56                       &gStatusCodeCallbackGuid,
57                       sizeof (EFI_PEI_RSC_HANDLER_CALLBACK) * 64 + sizeof (UINTN)
58                       );
59   ASSERT (NumberOfEntries != NULL);
60 
61   *NumberOfEntries = 0;
62 
63   return NumberOfEntries;
64 }
65 
66 /**
67   Register the callback function for ReportStatusCode() notification.
68 
69   When this function is called the function pointer is added to an internal list and any future calls to
70   ReportStatusCode() will be forwarded to the Callback function.
71 
72   @param[in] Callback           A pointer to a function of type EFI_PEI_RSC_HANDLER_CALLBACK that is called
73                                 when a call to ReportStatusCode() occurs.
74 
75   @retval EFI_SUCCESS           Function was successfully registered.
76   @retval EFI_INVALID_PARAMETER The callback function was NULL.
77   @retval EFI_OUT_OF_RESOURCES  The internal buffer ran out of space. No more functions can be
78                                 registered.
79   @retval EFI_ALREADY_STARTED   The function was already registered. It can't be registered again.
80 
81 **/
82 EFI_STATUS
83 EFIAPI
Register(IN EFI_PEI_RSC_HANDLER_CALLBACK Callback)84 Register (
85   IN EFI_PEI_RSC_HANDLER_CALLBACK Callback
86   )
87 {
88   EFI_PEI_HOB_POINTERS          Hob;
89   EFI_PEI_RSC_HANDLER_CALLBACK  *CallbackEntry;
90   UINTN                         *NumberOfEntries;
91   UINTN                         Index;
92   UINTN                         FreeEntryIndex;
93   UINTN                         *FreePacket;
94 
95   if (Callback == NULL) {
96     return EFI_INVALID_PARAMETER;
97   }
98 
99   Hob.Raw        = GetFirstGuidHob (&gStatusCodeCallbackGuid);
100   FreePacket     = NULL;
101   FreeEntryIndex = 0;
102   while (Hob.Raw != NULL) {
103     NumberOfEntries = GET_GUID_HOB_DATA (Hob);
104     CallbackEntry   = (EFI_PEI_RSC_HANDLER_CALLBACK *) (NumberOfEntries + 1);
105     if (FreePacket == NULL && *NumberOfEntries < 64) {
106       //
107       // If current total number of handlers does not exceed 64, put new handler
108       // at the last of packet
109       //
110       FreePacket = NumberOfEntries;
111       FreeEntryIndex = *NumberOfEntries;
112     }
113     for (Index = 0; Index < *NumberOfEntries; Index++) {
114       if (CallbackEntry[Index] == Callback) {
115         //
116         // If the function was already registered. It can't be registered again.
117         //
118         return EFI_ALREADY_STARTED;
119       }
120       if (FreePacket == NULL && CallbackEntry[Index] == NULL) {
121         //
122         // If the total number of handlers in current packet is max value 64,
123         // search an entry with NULL pointer and fill new handler into this entry.
124         //
125         FreePacket = NumberOfEntries;
126         FreeEntryIndex = Index;
127       }
128     }
129     Hob.Raw = GET_NEXT_HOB (Hob);
130     Hob.Raw = GetNextGuidHob (&gStatusCodeCallbackGuid, Hob.Raw);
131   }
132 
133   if (FreePacket == NULL) {
134     FreePacket = CreateRscHandlerCallbackPacket();
135   }
136 
137   CallbackEntry = (EFI_PEI_RSC_HANDLER_CALLBACK *) (FreePacket + 1);
138   CallbackEntry[FreeEntryIndex] = Callback;
139 
140   if (*FreePacket == FreeEntryIndex) {
141     //
142     // If new registered callback is added as a new entry in the packet,
143     // increase the total number of handlers in the packet.
144     //
145     *FreePacket += 1;
146   }
147 
148   return EFI_SUCCESS;
149 }
150 
151 /**
152   Remove a previously registered callback function from the notification list.
153 
154   ReportStatusCode() messages will no longer be forwarded to the Callback function.
155 
156   @param[in] Callback           A pointer to a function of type EFI_PEI_RSC_HANDLER_CALLBACK that is to be
157                                 unregistered.
158 
159   @retval EFI_SUCCESS           The function was successfully unregistered.
160   @retval EFI_INVALID_PARAMETER The callback function was NULL.
161   @retval EFI_NOT_FOUND         The callback function was not found to be unregistered.
162 
163 **/
164 EFI_STATUS
165 EFIAPI
Unregister(IN EFI_PEI_RSC_HANDLER_CALLBACK Callback)166 Unregister (
167   IN EFI_PEI_RSC_HANDLER_CALLBACK Callback
168   )
169 {
170   EFI_PEI_HOB_POINTERS            Hob;
171   EFI_PEI_RSC_HANDLER_CALLBACK    *CallbackEntry;
172   UINTN                           *NumberOfEntries;
173   UINTN                           Index;
174 
175   if (Callback == NULL) {
176     return EFI_INVALID_PARAMETER;
177   }
178 
179   Hob.Raw  = GetFirstGuidHob (&gStatusCodeCallbackGuid);
180   while (Hob.Raw != NULL) {
181     NumberOfEntries = GET_GUID_HOB_DATA (Hob);
182     CallbackEntry   = (EFI_PEI_RSC_HANDLER_CALLBACK *) (NumberOfEntries + 1);
183     for (Index = 0; Index < *NumberOfEntries; Index++) {
184       if (CallbackEntry[Index] == Callback) {
185         //
186         // Set removed entry as NULL.
187         //
188         CallbackEntry[Index] = NULL;
189         return EFI_SUCCESS;
190       }
191     }
192     Hob.Raw = GET_NEXT_HOB (Hob);
193     Hob.Raw = GetNextGuidHob (&gStatusCodeCallbackGuid, Hob.Raw);
194   }
195 
196   return EFI_NOT_FOUND;
197 }
198 
199 /**
200   Publishes an interface that allows PEIMs to report status codes.
201 
202   This function implements EFI_PEI_PROGRESS_CODE_PPI.ReportStatusCode().
203   It publishes an interface that allows PEIMs to report status codes.
204 
205   @param  PeiServices      An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
206   @param  CodeType         Indicates the type of status code being reported.
207   @param  Value            Describes the current status of a hardware or
208                            software entity. This includes information about the class and
209                            subclass that is used to classify the entity as well as an operation.
210                            For progress codes, the operation is the current activity.
211                            For error codes, it is the exception.For debug codes,it is not defined at this time.
212   @param  Instance         The enumeration of a hardware or software entity within
213                            the system. A system may contain multiple entities that match a class/subclass
214                            pairing. The instance differentiates between them. An instance of 0 indicates
215                            that instance information is unavailable, not meaningful, or not relevant.
216                            Valid instance numbers start with 1.
217   @param  CallerId         This optional parameter may be used to identify the caller.
218                            This parameter allows the status code driver to apply different rules to
219                            different callers.
220   @param  Data             This optional parameter may be used to pass additional data.
221 
222   @retval EFI_SUCCESS      The function completed successfully.
223 
224 **/
225 EFI_STATUS
226 EFIAPI
ReportDispatcher(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 OPTIONAL,IN CONST EFI_STATUS_CODE_DATA * Data OPTIONAL)227 ReportDispatcher (
228   IN CONST EFI_PEI_SERVICES         **PeiServices,
229   IN EFI_STATUS_CODE_TYPE           CodeType,
230   IN EFI_STATUS_CODE_VALUE          Value,
231   IN UINT32                         Instance,
232   IN CONST EFI_GUID                 *CallerId OPTIONAL,
233   IN CONST EFI_STATUS_CODE_DATA     *Data OPTIONAL
234   )
235 {
236   EFI_PEI_HOB_POINTERS            Hob;
237   EFI_PEI_RSC_HANDLER_CALLBACK    *CallbackEntry;
238   UINTN                           *NumberOfEntries;
239   UINTN                           Index;
240 
241   Hob.Raw  = GetFirstGuidHob (&gStatusCodeCallbackGuid);
242   while (Hob.Raw != NULL) {
243     NumberOfEntries = GET_GUID_HOB_DATA (Hob);
244     CallbackEntry   = (EFI_PEI_RSC_HANDLER_CALLBACK *) (NumberOfEntries + 1);
245     for (Index = 0; Index < *NumberOfEntries; Index++) {
246       if (CallbackEntry[Index] != NULL) {
247       CallbackEntry[Index](
248         PeiServices,
249         CodeType,
250         Value,
251         Instance,
252         CallerId,
253         Data
254         );
255       }
256     }
257     Hob.Raw = GET_NEXT_HOB (Hob);
258     Hob.Raw = GetNextGuidHob (&gStatusCodeCallbackGuid, Hob.Raw);
259   }
260 
261   return EFI_SUCCESS;
262 }
263 
264 /**
265   Entry point of Status Code PEIM.
266 
267   This function is the entry point of this Status Code Router PEIM.
268   It produces Report Stataus Code Handler PPI and Status Code PPI.
269 
270   @param  FileHandle  Handle of the file being invoked.
271   @param  PeiServices Describes the list of possible PEI Services.
272 
273   @retval EFI_SUCESS  The entry point of DXE IPL PEIM executes successfully.
274 
275 **/
276 EFI_STATUS
277 EFIAPI
GenericStatusCodePeiEntry(IN EFI_PEI_FILE_HANDLE FileHandle,IN CONST EFI_PEI_SERVICES ** PeiServices)278 GenericStatusCodePeiEntry (
279   IN       EFI_PEI_FILE_HANDLE  FileHandle,
280   IN CONST EFI_PEI_SERVICES     **PeiServices
281   )
282 {
283   EFI_STATUS                 Status;
284   EFI_PEI_PPI_DESCRIPTOR     *OldDescriptor;
285   EFI_PEI_PROGRESS_CODE_PPI  *OldStatusCodePpi;
286 
287   CreateRscHandlerCallbackPacket ();
288 
289   //
290   // Install Report Status Code Handler PPI
291   //
292   Status = PeiServicesInstallPpi (mRscHandlerPpiList);
293   ASSERT_EFI_ERROR (Status);
294 
295   //
296   // Install Status Code PPI. PI spec specifies that there can be only one instance
297   // of this PPI in system. So first check if other instance already exists.
298   // If no other instance exists, then just install the PPI.
299   // If other instance already exists, then reinstall it.
300   //
301   Status = PeiServicesLocatePpi (
302              &gEfiPeiStatusCodePpiGuid,
303              0,
304              &OldDescriptor,
305              (VOID **) &OldStatusCodePpi
306              );
307   if (!EFI_ERROR (Status)) {
308     Status = PeiServicesReInstallPpi (OldDescriptor, mStatusCodePpiList);
309   } else {
310     Status = PeiServicesInstallPpi (mStatusCodePpiList);
311   }
312   ASSERT_EFI_ERROR (Status);
313 
314   return EFI_SUCCESS;
315 }
316