1/** @file
2  Report Status Code Library for SMM Phase.
3
4  Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
5  This program and the accompanying materials
6  are licensed and made available under the terms and conditions of the BSD License
7  which accompanies this distribution.  The full text of the license may be found at
8  http://opensource.org/licenses/bsd-license.php
9
10  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include <Library/ReportStatusCodeLib.h>
16#include <Library/DebugLib.h>
17#include <Library/SmmServicesTableLib.h>
18#include <Library/BaseLib.h>
19#include <Library/BaseMemoryLib.h>
20#include <Library/PcdLib.h>
21#include <Library/MemoryAllocationLib.h>
22
23#include <Guid/StatusCodeDataTypeId.h>
24#include <Guid/StatusCodeDataTypeDebug.h>
25#include <Protocol/SmmStatusCode.h>
26
27EFI_SMM_REPORT_STATUS_CODE     mReportStatusCode = NULL;
28EFI_SMM_STATUS_CODE_PROTOCOL   *mStatusCodeProtocol = NULL;
29
30
31/**
32  Locate the report status code service.
33
34  @return   Function pointer to the report status code service.
35            NULL is returned if no status code service is available.
36
37**/
38EFI_SMM_REPORT_STATUS_CODE
39InternalGetReportStatusCode (
40  VOID
41  )
42{
43  EFI_STATUS                    Status;
44
45  Status = gSmst->SmmLocateProtocol (&gEfiSmmStatusCodeProtocolGuid, NULL, (VOID**)&mStatusCodeProtocol);
46  if (!EFI_ERROR (Status) && mStatusCodeProtocol != NULL) {
47    return mStatusCodeProtocol->ReportStatusCode;
48  }
49  return NULL;
50}
51
52/**
53  Internal worker function that reports a status code through the status code service.
54
55  If status code service is not cached, then this function checks if status code service is
56  available in system.  If status code service is not available, then EFI_UNSUPPORTED is
57  returned.  If status code service is present, then it is cached in mReportStatusCode.
58  Finally this function reports status code through the status code service.
59
60  @param  Type              Status code type.
61  @param  Value             Status code value.
62  @param  Instance          Status code instance number.
63  @param  CallerId          Pointer to a GUID that identifies the caller of this
64                            function.  This is an optional parameter that may be
65                            NULL.
66  @param  Data              Pointer to the extended data buffer.  This is an
67                            optional parameter that may be NULL.
68
69  @retval EFI_SUCCESS       The status code was reported.
70  @retval EFI_UNSUPPORTED   Status code service is not available.
71  @retval EFI_UNSUPPORTED   Status code type is not supported.
72
73**/
74EFI_STATUS
75InternalReportStatusCode (
76  IN EFI_STATUS_CODE_TYPE     Type,
77  IN EFI_STATUS_CODE_VALUE    Value,
78  IN UINT32                   Instance,
79  IN CONST EFI_GUID           *CallerId OPTIONAL,
80  IN EFI_STATUS_CODE_DATA     *Data     OPTIONAL
81  )
82{
83  if ((ReportProgressCodeEnabled() && ((Type) & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE) ||
84      (ReportErrorCodeEnabled() && ((Type) & EFI_STATUS_CODE_TYPE_MASK) == EFI_ERROR_CODE) ||
85      (ReportDebugCodeEnabled() && ((Type) & EFI_STATUS_CODE_TYPE_MASK) == EFI_DEBUG_CODE)) {
86    //
87    // If mReportStatusCode is NULL, then check if status code service is available in system.
88    //
89    if (mReportStatusCode == NULL) {
90      mReportStatusCode = InternalGetReportStatusCode ();
91      if (mReportStatusCode == NULL) {
92        return EFI_UNSUPPORTED;
93      }
94    }
95
96    //
97    // A status code service is present in system, so pass in all the parameters to the service.
98    //
99    return (*mReportStatusCode) (mStatusCodeProtocol, Type, Value, Instance, (EFI_GUID *)CallerId, Data);
100  }
101
102  return EFI_UNSUPPORTED;
103}
104
105
106/**
107  Converts a status code to an 8-bit POST code value.
108
109  Converts the status code specified by CodeType and Value to an 8-bit POST code
110  and returns the 8-bit POST code in PostCode.  If CodeType is an
111  EFI_PROGRESS_CODE or CodeType is an EFI_ERROR_CODE, then bits 0..4 of PostCode
112  are set to bits 16..20 of Value, and bits 5..7 of PostCode are set to bits
113  24..26 of Value., and TRUE is returned.  Otherwise, FALSE is returned.
114
115  If PostCode is NULL, then ASSERT().
116
117  @param  CodeType  The type of status code being converted.
118  @param  Value     The status code value being converted.
119  @param  PostCode  A pointer to the 8-bit POST code value to return.
120
121  @retval  TRUE   The status code specified by CodeType and Value was converted
122                  to an 8-bit POST code and returned in  PostCode.
123  @retval  FALSE  The status code specified by CodeType and Value could not be
124                  converted to an 8-bit POST code value.
125
126**/
127BOOLEAN
128EFIAPI
129CodeTypeToPostCode (
130  IN  EFI_STATUS_CODE_TYPE   CodeType,
131  IN  EFI_STATUS_CODE_VALUE  Value,
132  OUT UINT8                  *PostCode
133  )
134{
135  //
136  // If PostCode is NULL, then ASSERT()
137  //
138  ASSERT (PostCode != NULL);
139
140  //
141  // Convert Value to an 8 bit post code
142  //
143  if (((CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE) ||
144      ((CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_ERROR_CODE)       ) {
145    *PostCode  = (UINT8) ((((Value & EFI_STATUS_CODE_CLASS_MASK) >> 24) << 5) |
146                          (((Value & EFI_STATUS_CODE_SUBCLASS_MASK) >> 16) & 0x1f));
147    return TRUE;
148  }
149  return FALSE;
150}
151
152
153/**
154  Extracts ASSERT() information from a status code structure.
155
156  Converts the status code specified by CodeType, Value, and Data to the ASSERT()
157  arguments specified by Filename, Description, and LineNumber.  If CodeType is
158  an EFI_ERROR_CODE, and CodeType has a severity of EFI_ERROR_UNRECOVERED, and
159  Value has an operation mask of EFI_SW_EC_ILLEGAL_SOFTWARE_STATE, extract
160  Filename, Description, and LineNumber from the optional data area of the
161  status code buffer specified by Data.  The optional data area of Data contains
162  a Null-terminated ASCII string for the FileName, followed by a Null-terminated
163  ASCII string for the Description, followed by a 32-bit LineNumber.  If the
164  ASSERT() information could be extracted from Data, then return TRUE.
165  Otherwise, FALSE is returned.
166
167  If Data is NULL, then ASSERT().
168  If Filename is NULL, then ASSERT().
169  If Description is NULL, then ASSERT().
170  If LineNumber is NULL, then ASSERT().
171
172  @param  CodeType     The type of status code being converted.
173  @param  Value        The status code value being converted.
174  @param  Data         Pointer to status code data buffer.
175  @param  Filename     Pointer to the source file name that generated the ASSERT().
176  @param  Description  Pointer to the description of the ASSERT().
177  @param  LineNumber   Pointer to source line number that generated the ASSERT().
178
179  @retval  TRUE   The status code specified by CodeType, Value, and Data was
180                  converted ASSERT() arguments specified by Filename, Description,
181                  and LineNumber.
182  @retval  FALSE  The status code specified by CodeType, Value, and Data could
183                  not be converted to ASSERT() arguments.
184
185**/
186BOOLEAN
187EFIAPI
188ReportStatusCodeExtractAssertInfo (
189  IN EFI_STATUS_CODE_TYPE        CodeType,
190  IN EFI_STATUS_CODE_VALUE       Value,
191  IN CONST EFI_STATUS_CODE_DATA  *Data,
192  OUT CHAR8                      **Filename,
193  OUT CHAR8                      **Description,
194  OUT UINT32                     *LineNumber
195  )
196{
197  EFI_DEBUG_ASSERT_DATA  *AssertData;
198
199  ASSERT (Data        != NULL);
200  ASSERT (Filename    != NULL);
201  ASSERT (Description != NULL);
202  ASSERT (LineNumber  != NULL);
203
204  if (((CodeType & EFI_STATUS_CODE_TYPE_MASK)      == EFI_ERROR_CODE) &&
205      ((CodeType & EFI_STATUS_CODE_SEVERITY_MASK)  == EFI_ERROR_UNRECOVERED) &&
206      ((Value    & EFI_STATUS_CODE_OPERATION_MASK) == EFI_SW_EC_ILLEGAL_SOFTWARE_STATE)) {
207    AssertData   = (EFI_DEBUG_ASSERT_DATA *)(Data + 1);
208    *Filename    = (CHAR8 *)(AssertData + 1);
209    *Description = *Filename + AsciiStrLen (*Filename) + 1;
210    *LineNumber  = AssertData->LineNumber;
211    return TRUE;
212  }
213  return FALSE;
214}
215
216
217/**
218  Extracts DEBUG() information from a status code structure.
219
220  Converts the status code specified by Data to the DEBUG() arguments specified
221  by ErrorLevel, Marker, and Format.  If type GUID in Data is
222  EFI_STATUS_CODE_DATA_TYPE_DEBUG_GUID, then extract ErrorLevel, Marker, and
223  Format from the optional data area of the status code buffer specified by Data.
224  The optional data area of Data contains a 32-bit ErrorLevel followed by Marker
225  which is 12 UINTN parameters, followed by a Null-terminated ASCII string for
226  the Format.  If the DEBUG() information could be extracted from Data, then
227  return TRUE.  Otherwise, FALSE is returned.
228
229  If Data is NULL, then ASSERT().
230  If ErrorLevel is NULL, then ASSERT().
231  If Marker is NULL, then ASSERT().
232  If Format is NULL, then ASSERT().
233
234  @param  Data        Pointer to status code data buffer.
235  @param  ErrorLevel  Pointer to error level mask for a debug message.
236  @param  Marker      Pointer to the variable argument list associated with Format.
237  @param  Format      Pointer to a Null-terminated ASCII format string of a
238                      debug message.
239
240  @retval  TRUE   The status code specified by Data was converted DEBUG() arguments
241                  specified by ErrorLevel, Marker, and Format.
242  @retval  FALSE  The status code specified by Data could not be converted to
243                  DEBUG() arguments.
244
245**/
246BOOLEAN
247EFIAPI
248ReportStatusCodeExtractDebugInfo (
249  IN CONST EFI_STATUS_CODE_DATA  *Data,
250  OUT UINT32                     *ErrorLevel,
251  OUT BASE_LIST                  *Marker,
252  OUT CHAR8                      **Format
253  )
254{
255  EFI_DEBUG_INFO  *DebugInfo;
256
257  ASSERT (Data       != NULL);
258  ASSERT (ErrorLevel != NULL);
259  ASSERT (Marker     != NULL);
260  ASSERT (Format     != NULL);
261
262  //
263  // If the GUID type is not EFI_STATUS_CODE_DATA_TYPE_DEBUG_GUID then return FALSE
264  //
265  if (!CompareGuid (&Data->Type, &gEfiStatusCodeDataTypeDebugGuid)) {
266    return FALSE;
267  }
268
269  //
270  // Retrieve the debug information from the status code record
271  //
272  DebugInfo = (EFI_DEBUG_INFO *)(Data + 1);
273
274  *ErrorLevel = DebugInfo->ErrorLevel;
275
276  //
277  // The first 12 * sizeof (UINT64) bytes following EFI_DEBUG_INFO are for variable arguments
278  // of format in DEBUG string. Its address is returned in Marker and has to be 64-bit aligned.
279  // It must be noticed that EFI_DEBUG_INFO follows EFI_STATUS_CODE_DATA, whose size is
280  // 20 bytes. The size of EFI_DEBUG_INFO is 4 bytes, so we can ensure that Marker
281  // returned is 64-bit aligned.
282  // 64-bit aligned is a must, otherwise retrieving 64-bit parameter from BASE_LIST will
283  // cause unalignment exception.
284  //
285  *Marker = (BASE_LIST) (DebugInfo + 1);
286  *Format = (CHAR8 *)(((UINT64 *)*Marker) + 12);
287
288  return TRUE;
289}
290
291
292/**
293  Reports a status code.
294
295  Reports the status code specified by the parameters Type and Value.  Status
296  code also require an instance, caller ID, and extended data.  This function
297  passed in a zero instance, NULL extended data, and a caller ID of
298  gEfiCallerIdGuid, which is the GUID for the module.
299
300  ReportStatusCode()must actively prevent recusrsion.  If ReportStatusCode()
301  is called while processing another any other Report Status Code Library function,
302  then ReportStatusCode() must return immediately.
303
304  @param  Type   Status code type.
305  @param  Value  Status code value.
306
307  @retval  EFI_SUCCESS       The status code was reported.
308  @retval  EFI_DEVICE_ERROR  There status code could not be reported due to a
309                             device error.
310  @retval  EFI_UNSUPPORTED   Report status code is not supported
311
312**/
313EFI_STATUS
314EFIAPI
315ReportStatusCode (
316  IN EFI_STATUS_CODE_TYPE   Type,
317  IN EFI_STATUS_CODE_VALUE  Value
318  )
319{
320  return InternalReportStatusCode (Type, Value, 0, &gEfiCallerIdGuid, NULL);
321}
322
323
324/**
325  Reports a status code with an extended data buffer.
326
327  Allocates and fills in the extended data section of a status code with the
328  extended data specified by ExtendedData and ExtendedDataSize.  ExtendedData
329  is assumed to be one of the data structures specified in Related Definitions.
330  These data structure do not have the standard header, so this function is
331  responsible for allocating a buffer large enough for the standard header and
332  the extended data passed into this function.  The standard header is filled
333  in with a GUID of  gEfiStatusCodeSpecificDataGuid.  The status code is reported
334  with a zero instance and a caller ID of gEfiCallerIdGuid.
335
336  ReportStatusCodeWithExtendedData()must actively prevent recursion.  If
337  ReportStatusCodeWithExtendedData() is called while processing another any other
338  Report Status Code Library function, then ReportStatusCodeWithExtendedData()
339  must return EFI_DEVICE_ERROR immediately.
340
341  If ExtendedData is NULL, then ASSERT().
342  If ExtendedDataSize is 0, then ASSERT().
343
344  @param  Type              Status code type.
345  @param  Value             Status code value.
346  @param  ExtendedData      Pointer to the extended data buffer to be reported.
347  @param  ExtendedDataSize  The size, in bytes, of the extended data buffer to
348                            be reported.
349
350  @retval  EFI_SUCCESS           The status code was reported with the extended
351                                 data specified by ExtendedData and ExtendedDataSize.
352  @retval  EFI_OUT_OF_RESOURCES  There were not enough resources to allocate the
353                                 extended data section.
354  @retval  EFI_UNSUPPORTED       Report status code is not supported
355
356**/
357EFI_STATUS
358EFIAPI
359ReportStatusCodeWithExtendedData (
360  IN EFI_STATUS_CODE_TYPE   Type,
361  IN EFI_STATUS_CODE_VALUE  Value,
362  IN CONST VOID             *ExtendedData,
363  IN UINTN                  ExtendedDataSize
364  )
365{
366  ASSERT (ExtendedData     != NULL);
367  ASSERT (ExtendedDataSize != 0);
368  return ReportStatusCodeEx (
369           Type,
370           Value,
371           0,
372           NULL,
373           NULL,
374           ExtendedData,
375           ExtendedDataSize
376           );
377}
378
379
380/**
381  Reports a status code with full parameters.
382
383  The function reports a status code.  If ExtendedData is NULL and ExtendedDataSize
384  is 0, then an extended data buffer is not reported.  If ExtendedData is not
385  NULL and ExtendedDataSize is not 0, then an extended data buffer is allocated.
386  ExtendedData is assumed not have the standard status code header, so this function
387  is responsible for allocating a buffer large enough for the standard header and
388  the extended data passed into this function.  The standard header is filled in
389  with a GUID specified by ExtendedDataGuid.  If ExtendedDataGuid is NULL, then a
390  GUID of gEfiStatusCodeSpecificDataGuid is used.  The status code is reported with
391  an instance specified by Instance and a caller ID specified by CallerId.  If
392  CallerId is NULL, then a caller ID of gEfiCallerIdGuid is used.
393
394  ReportStatusCodeEx()must actively prevent recursion. If
395  ReportStatusCodeEx() is called while processing another any
396  other Report Status Code Library function, then
397  ReportStatusCodeEx() must return EFI_DEVICE_ERROR immediately.
398
399  If ExtendedData is NULL and ExtendedDataSize is not zero, then ASSERT().
400  If ExtendedData is not NULL and ExtendedDataSize is zero, then ASSERT().
401
402  @param  Type              Status code type.
403  @param  Value             Status code value.
404  @param  Instance          Status code instance number.
405  @param  CallerId          Pointer to a GUID that identifies the caller of this
406                            function.  If this parameter is NULL, then a caller
407                            ID of gEfiCallerIdGuid is used.
408  @param  ExtendedDataGuid  Pointer to the GUID for the extended data buffer.
409                            If this parameter is NULL, then a the status code
410                            standard header is filled in with
411                            gEfiStatusCodeSpecificDataGuid.
412  @param  ExtendedData      Pointer to the extended data buffer.  This is an
413                            optional parameter that may be NULL.
414  @param  ExtendedDataSize  The size, in bytes, of the extended data buffer.
415
416  @retval  EFI_SUCCESS           The status code was reported.
417  @retval  EFI_OUT_OF_RESOURCES  There were not enough resources to allocate
418                                 the extended data section if it was specified.
419  @retval  EFI_UNSUPPORTED       Report status code is not supported
420
421**/
422EFI_STATUS
423EFIAPI
424ReportStatusCodeEx (
425  IN EFI_STATUS_CODE_TYPE   Type,
426  IN EFI_STATUS_CODE_VALUE  Value,
427  IN UINT32                 Instance,
428  IN CONST EFI_GUID         *CallerId          OPTIONAL,
429  IN CONST EFI_GUID         *ExtendedDataGuid  OPTIONAL,
430  IN CONST VOID             *ExtendedData      OPTIONAL,
431  IN UINTN                  ExtendedDataSize
432  )
433{
434  EFI_STATUS            Status;
435  EFI_STATUS_CODE_DATA  *StatusCodeData;
436
437  ASSERT (!((ExtendedData == NULL) && (ExtendedDataSize != 0)));
438  ASSERT (!((ExtendedData != NULL) && (ExtendedDataSize == 0)));
439
440  //
441  // Allocate space for the Status Code Header and its buffer
442  //
443  StatusCodeData = AllocatePool (sizeof (EFI_STATUS_CODE_DATA) + ExtendedDataSize);
444  if (StatusCodeData == NULL) {
445    return EFI_OUT_OF_RESOURCES;
446  }
447
448  //
449  // Fill in the extended data header
450  //
451  StatusCodeData->HeaderSize = (UINT16) sizeof (EFI_STATUS_CODE_DATA);
452  StatusCodeData->Size = (UINT16) ExtendedDataSize;
453  if (ExtendedDataGuid == NULL) {
454    ExtendedDataGuid = &gEfiStatusCodeSpecificDataGuid;
455  }
456  CopyGuid (&StatusCodeData->Type, ExtendedDataGuid);
457
458  //
459  // Fill in the extended data buffer
460  //
461  if (ExtendedData != NULL) {
462    CopyMem (StatusCodeData + 1, ExtendedData, ExtendedDataSize);
463  }
464
465  //
466  // Report the status code
467  //
468  if (CallerId == NULL) {
469    CallerId = &gEfiCallerIdGuid;
470  }
471  Status = InternalReportStatusCode (Type, Value, Instance, CallerId, StatusCodeData);
472
473  //
474  // Free the allocated buffer
475  //
476  FreePool (StatusCodeData);
477
478  return Status;
479}
480
481
482/**
483  Returns TRUE if status codes of type EFI_PROGRESS_CODE are enabled
484
485  This function returns TRUE if the REPORT_STATUS_CODE_PROPERTY_PROGRESS_CODE_ENABLED
486  bit of PcdReportStatusCodeProperyMask is set.  Otherwise FALSE is returned.
487
488  @retval  TRUE   The REPORT_STATUS_CODE_PROPERTY_PROGRESS_CODE_ENABLED bit of
489                  PcdReportStatusCodeProperyMask is set.
490  @retval  FALSE  The REPORT_STATUS_CODE_PROPERTY_PROGRESS_CODE_ENABLED bit of
491                  PcdReportStatusCodeProperyMask is clear.
492
493**/
494BOOLEAN
495EFIAPI
496ReportProgressCodeEnabled (
497  VOID
498  )
499{
500  return (BOOLEAN) ((PcdGet8 (PcdReportStatusCodePropertyMask) & REPORT_STATUS_CODE_PROPERTY_PROGRESS_CODE_ENABLED) != 0);
501}
502
503
504/**
505  Returns TRUE if status codes of type EFI_ERROR_CODE are enabled
506
507  This function returns TRUE if the REPORT_STATUS_CODE_PROPERTY_ERROR_CODE_ENABLED
508  bit of PcdReportStatusCodeProperyMask is set.  Otherwise FALSE is returned.
509
510  @retval  TRUE   The REPORT_STATUS_CODE_PROPERTY_ERROR_CODE_ENABLED bit of
511                  PcdReportStatusCodeProperyMask is set.
512  @retval  FALSE  The REPORT_STATUS_CODE_PROPERTY_ERROR_CODE_ENABLED bit of
513                  PcdReportStatusCodeProperyMask is clear.
514
515**/
516BOOLEAN
517EFIAPI
518ReportErrorCodeEnabled (
519  VOID
520  )
521{
522  return (BOOLEAN) ((PcdGet8 (PcdReportStatusCodePropertyMask) & REPORT_STATUS_CODE_PROPERTY_ERROR_CODE_ENABLED) != 0);
523}
524
525
526/**
527  Returns TRUE if status codes of type EFI_DEBUG_CODE are enabled
528
529  This function returns TRUE if the REPORT_STATUS_CODE_PROPERTY_DEBUG_CODE_ENABLED
530  bit of PcdReportStatusCodeProperyMask is set.  Otherwise FALSE is returned.
531
532  @retval  TRUE   The REPORT_STATUS_CODE_PROPERTY_DEBUG_CODE_ENABLED bit of
533                  PcdReportStatusCodeProperyMask is set.
534  @retval  FALSE  The REPORT_STATUS_CODE_PROPERTY_DEBUG_CODE_ENABLED bit of
535                  PcdReportStatusCodeProperyMask is clear.
536
537**/
538BOOLEAN
539EFIAPI
540ReportDebugCodeEnabled (
541  VOID
542  )
543{
544  return (BOOLEAN) ((PcdGet8 (PcdReportStatusCodePropertyMask) & REPORT_STATUS_CODE_PROPERTY_DEBUG_CODE_ENABLED) != 0);
545}
546