1 /** @file
2   Library that helps implement monolithic PEI
3 
4   Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include <PrePi.h>
17 #include <Library/ReportStatusCodeLib.h>
18 #include <Library/SerialPortLib.h>
19 #include <Library/PrintLib.h>
20 
21 #include <Protocol/StatusCode.h>
22 #include <Guid/StatusCodeDataTypeId.h>
23 #include <Guid/StatusCodeDataTypeDebug.h>
24 #include <FrameworkPei.h>
25 
26 EFI_STATUS
27 EFIAPI
28 SerialReportStatusCode (
29   IN EFI_STATUS_CODE_TYPE             CodeType,
30   IN EFI_STATUS_CODE_VALUE            Value,
31   IN UINT32                           Instance,
32   IN CONST EFI_GUID                   *CallerId,
33   IN CONST EFI_STATUS_CODE_DATA       *Data OPTIONAL
34   );
35 
36 
37 EFI_STATUS_CODE_PROTOCOL gStatusCode = {
38   (EFI_REPORT_STATUS_CODE)SerialReportStatusCode
39 };
40 
41 /**
42   Extracts ASSERT() information from a status code structure.
43 
44   Converts the status code specified by CodeType, Value, and Data to the ASSERT()
45   arguments specified by Filename, Description, and LineNumber.  If CodeType is
46   an EFI_ERROR_CODE, and CodeType has a severity of EFI_ERROR_UNRECOVERED, and
47   Value has an operation mask of EFI_SW_EC_ILLEGAL_SOFTWARE_STATE, extract
48   Filename, Description, and LineNumber from the optional data area of the
49   status code buffer specified by Data.  The optional data area of Data contains
50   a Null-terminated ASCII string for the FileName, followed by a Null-terminated
51   ASCII string for the Description, followed by a 32-bit LineNumber.  If the
52   ASSERT() information could be extracted from Data, then return TRUE.
53   Otherwise, FALSE is returned.
54 
55   If Data is NULL, then ASSERT().
56   If Filename is NULL, then ASSERT().
57   If Description is NULL, then ASSERT().
58   If LineNumber is NULL, then ASSERT().
59 
60   @param  CodeType     The type of status code being converted.
61   @param  Value        The status code value being converted.
62   @param  Data         Pointer to status code data buffer.
63   @param  Filename     Pointer to the source file name that generated the ASSERT().
64   @param  Description  Pointer to the description of the ASSERT().
65   @param  LineNumber   Pointer to source line number that generated the ASSERT().
66 
67   @retval  TRUE   The status code specified by CodeType, Value, and Data was
68                   converted ASSERT() arguments specified by Filename, Description,
69                   and LineNumber.
70   @retval  FALSE  The status code specified by CodeType, Value, and Data could
71                   not be converted to ASSERT() arguments.
72 
73 **/
74 BOOLEAN
75 EFIAPI
ReportStatusCodeExtractAssertInfo(IN EFI_STATUS_CODE_TYPE CodeType,IN EFI_STATUS_CODE_VALUE Value,IN CONST EFI_STATUS_CODE_DATA * Data,OUT CHAR8 ** Filename,OUT CHAR8 ** Description,OUT UINT32 * LineNumber)76 ReportStatusCodeExtractAssertInfo (
77   IN EFI_STATUS_CODE_TYPE        CodeType,
78   IN EFI_STATUS_CODE_VALUE       Value,
79   IN CONST EFI_STATUS_CODE_DATA  *Data,
80   OUT CHAR8                      **Filename,
81   OUT CHAR8                      **Description,
82   OUT UINT32                     *LineNumber
83   )
84 {
85   EFI_DEBUG_ASSERT_DATA  *AssertData;
86 
87   ASSERT (Data        != NULL);
88   ASSERT (Filename    != NULL);
89   ASSERT (Description != NULL);
90   ASSERT (LineNumber  != NULL);
91 
92   if (((CodeType & EFI_STATUS_CODE_TYPE_MASK)      == EFI_ERROR_CODE) &&
93       ((CodeType & EFI_STATUS_CODE_SEVERITY_MASK)  == EFI_ERROR_UNRECOVERED) &&
94       ((Value    & EFI_STATUS_CODE_OPERATION_MASK) == EFI_SW_EC_ILLEGAL_SOFTWARE_STATE)) {
95     AssertData   = (EFI_DEBUG_ASSERT_DATA *)(Data + 1);
96     *Filename    = (CHAR8 *)(AssertData + 1);
97     *Description = *Filename + AsciiStrLen (*Filename) + 1;
98     *LineNumber  = AssertData->LineNumber;
99     return TRUE;
100   }
101   return FALSE;
102 }
103 
104 
105 /**
106   Extracts DEBUG() information from a status code structure.
107 
108   Converts the status code specified by Data to the DEBUG() arguments specified
109   by ErrorLevel, Marker, and Format.  If type GUID in Data is
110   EFI_STATUS_CODE_DATA_TYPE_DEBUG_GUID, then extract ErrorLevel, Marker, and
111   Format from the optional data area of the status code buffer specified by Data.
112   The optional data area of Data contains a 32-bit ErrorLevel followed by Marker
113   which is 12 UINTN parameters, followed by a Null-terminated ASCII string for
114   the Format.  If the DEBUG() information could be extracted from Data, then
115   return TRUE.  Otherwise, FALSE is returned.
116 
117   If Data is NULL, then ASSERT().
118   If ErrorLevel is NULL, then ASSERT().
119   If Marker is NULL, then ASSERT().
120   If Format is NULL, then ASSERT().
121 
122   @param  Data        Pointer to status code data buffer.
123   @param  ErrorLevel  Pointer to error level mask for a debug message.
124   @param  Marker      Pointer to the variable argument list associated with Format.
125   @param  Format      Pointer to a Null-terminated ASCII format string of a
126                       debug message.
127 
128   @retval  TRUE   The status code specified by Data was converted DEBUG() arguments
129                   specified by ErrorLevel, Marker, and Format.
130   @retval  FALSE  The status code specified by Data could not be converted to
131                   DEBUG() arguments.
132 
133 **/
134 BOOLEAN
135 EFIAPI
ReportStatusCodeExtractDebugInfo(IN CONST EFI_STATUS_CODE_DATA * Data,OUT UINT32 * ErrorLevel,OUT BASE_LIST * Marker,OUT CHAR8 ** Format)136 ReportStatusCodeExtractDebugInfo (
137   IN CONST EFI_STATUS_CODE_DATA  *Data,
138   OUT UINT32                     *ErrorLevel,
139   OUT BASE_LIST                  *Marker,
140   OUT CHAR8                      **Format
141   )
142 {
143   EFI_DEBUG_INFO  *DebugInfo;
144 
145   ASSERT (Data          != NULL);
146   ASSERT (ErrorLevel    != NULL);
147   ASSERT (Marker     != NULL);
148   ASSERT (Format     != NULL);
149 
150   //
151   // If the GUID type is not EFI_STATUS_CODE_DATA_TYPE_DEBUG_GUID then return FALSE
152   //
153   if (!CompareGuid (&Data->Type, &gEfiStatusCodeDataTypeDebugGuid)) {
154     return FALSE;
155   }
156 
157   //
158   // Retrieve the debug information from the status code record
159   //
160   DebugInfo = (EFI_DEBUG_INFO *)(Data + 1);
161 
162   *ErrorLevel = DebugInfo->ErrorLevel;
163 
164   //
165   // The first 12 * UINTN bytes of the string are really an
166   // argument stack to support varargs on the Format string.
167   //
168   *Marker = (BASE_LIST) (DebugInfo + 1);
169   *Format = (CHAR8 *)(((UINT64 *)*Marker) + 12);
170 
171   return TRUE;
172 }
173 
174 
175 
176 
177 EFI_STATUS
178 EFIAPI
SerialReportStatusCode(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 OPTIONAL)179 SerialReportStatusCode (
180   IN EFI_STATUS_CODE_TYPE             CodeType,
181   IN EFI_STATUS_CODE_VALUE            Value,
182   IN UINT32                           Instance,
183   IN CONST EFI_GUID                   *CallerId,
184   IN CONST EFI_STATUS_CODE_DATA       *Data OPTIONAL
185   )
186 {
187   CHAR8           *Filename;
188   CHAR8           *Description;
189   CHAR8           *Format;
190   CHAR8           Buffer[EFI_STATUS_CODE_DATA_MAX_SIZE];
191   UINT32          ErrorLevel;
192   UINT32          LineNumber;
193   UINTN           CharCount;
194   BASE_LIST       Marker;
195   EFI_DEBUG_INFO  *DebugInfo;
196 
197   Buffer[0] = '\0';
198 
199 
200   if (Data != NULL &&
201     ReportStatusCodeExtractAssertInfo (CodeType, Value, Data, &Filename, &Description, &LineNumber)) {
202 
203     //
204     // Print ASSERT() information into output buffer.
205     //
206     CharCount = AsciiSPrint (
207                   Buffer,
208                   EFI_STATUS_CODE_DATA_MAX_SIZE,
209                   "\n\rASSERT!: %a (%d): %a\n\r",
210                   Filename,
211                   LineNumber,
212                   Description
213                   );
214 
215 
216     //
217     // Callout to standard output.
218     //
219     SerialPortWrite ((UINT8 *)Buffer, CharCount);
220     return EFI_SUCCESS;
221 
222   } else if (Data != NULL &&
223     ReportStatusCodeExtractDebugInfo (Data, &ErrorLevel, &Marker, &Format)) {
224 
225     //
226     // Print DEBUG() information into output buffer.
227     //
228     CharCount = AsciiBSPrint (
229                   Buffer,
230                   EFI_STATUS_CODE_DATA_MAX_SIZE,
231                   Format,
232                   Marker
233                   );
234 
235   } else if (Data != NULL &&
236              CompareGuid (&Data->Type, &gEfiStatusCodeSpecificDataGuid) &&
237              (CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_DEBUG_CODE) {
238 
239     //
240     // Print specific data into output buffer.
241     //
242     DebugInfo = (EFI_DEBUG_INFO *) (Data + 1);
243     Marker    = (BASE_LIST) (DebugInfo + 1);
244     Format    = (CHAR8 *) (((UINT64 *) (DebugInfo + 1)) + 12);
245 
246     CharCount = AsciiBSPrint (Buffer, EFI_STATUS_CODE_DATA_MAX_SIZE, Format, Marker);
247 
248   } else if ((CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_ERROR_CODE) {
249     //
250     // Print ERROR information into output buffer.
251     //
252 
253     CharCount = AsciiSPrint (
254                   Buffer,
255                   EFI_STATUS_CODE_DATA_MAX_SIZE,
256                   "ERROR: C%x:V%x I%x",
257                   CodeType,
258                   Value,
259                   Instance
260                   );
261 
262     //
263     // Make sure we don't try to print values that weren't intended to be printed, especially NULL GUID pointers.
264     //
265     if (CallerId != NULL) {
266       CharCount += AsciiSPrint (
267                      &Buffer[CharCount - 1],
268                      (EFI_STATUS_CODE_DATA_MAX_SIZE - (sizeof (Buffer[0]) * CharCount)),
269                      " %g",
270                      CallerId
271                      );
272     }
273 
274     if (Data != NULL) {
275       CharCount += AsciiSPrint (
276                      &Buffer[CharCount - 1],
277                      (EFI_STATUS_CODE_DATA_MAX_SIZE - (sizeof (Buffer[0]) * CharCount)),
278                      " %x",
279                      Data
280                      );
281 
282     }
283 
284 
285     CharCount += AsciiSPrint (
286                    &Buffer[CharCount - 1],
287                    (EFI_STATUS_CODE_DATA_MAX_SIZE - (sizeof (Buffer[0]) * CharCount)),
288                    "\n\r"
289                    );
290 
291   } else if ((CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE) {
292     CharCount = AsciiSPrint (
293                   Buffer,
294                   EFI_STATUS_CODE_DATA_MAX_SIZE,
295                   "PROGRESS CODE: V%x I%x\n\r",
296                   Value,
297                   Instance
298                   );
299   } else {
300     CharCount = AsciiSPrint (
301                   Buffer,
302                   EFI_STATUS_CODE_DATA_MAX_SIZE,
303                   "Undefined: C%x:V%x I%x\n\r",
304                   CodeType,
305                   Value,
306                   Instance
307                   );
308 
309   }
310 
311   SerialPortWrite ((UINT8 *)Buffer, CharCount);
312   return EFI_SUCCESS;
313 
314 }
315 
316 
317 VOID
318 EFIAPI
AddDxeCoreReportStatusCodeCallback(VOID)319 AddDxeCoreReportStatusCodeCallback (
320   VOID
321   )
322 {
323   BuildGuidDataHob (&gEfiStatusCodeRuntimeProtocolGuid, &gStatusCode, sizeof(VOID *));
324 }
325 
326