1 /*++ 2 3 Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR> 4 This program and the accompanying materials 5 are licensed and made available under the terms and conditions of the BSD License 6 which accompanies this distribution. The full text of the license may be found at 7 http://opensource.org/licenses/bsd-license.php 8 9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 11 12 Module Name: 13 14 BsDataHubStatusCode.c 15 16 Abstract: 17 18 This implements a status code listener that logs status codes into the data 19 hub. This is only active during non-runtime DXE. 20 The status codes are recorded in a extensible buffer, and a event is signalled 21 to log them to the data hub. The recorder is the producer of the status code in 22 buffer and the event notify function the consumer. 23 24 --*/ 25 26 #include "BsDataHubStatusCode.h" 27 28 // 29 // Initialize FIFO to cache records. 30 // 31 STATIC EFI_LIST_ENTRY mRecordsFifo = INITIALIZE_LIST_HEAD_VARIABLE (mRecordsFifo); 32 STATIC EFI_LIST_ENTRY mRecordsBuffer = INITIALIZE_LIST_HEAD_VARIABLE (mRecordsBuffer); 33 STATIC EFI_EVENT mLogDataHubEvent; 34 STATIC BOOLEAN mEventHandlerActive = FALSE; 35 36 // 37 // Cache data hub protocol. 38 // 39 STATIC EFI_DATA_HUB_PROTOCOL *mDataHubProtocol; 40 41 STATIC 42 DATA_HUB_STATUS_CODE_DATA_RECORD * 43 AcquireRecordBuffer ( 44 VOID 45 ) 46 /*++ 47 48 Routine Description: 49 50 Return one DATAHUB_STATUSCODE_RECORD space. 51 The size of free record pool would be extend, if the pool is empty. 52 53 Arguments: 54 55 None 56 57 Returns: 58 59 A pointer to the new allocated node or NULL if non available 60 61 --*/ 62 { 63 DATAHUB_STATUSCODE_RECORD *Record; 64 EFI_TPL CurrentTpl; 65 EFI_LIST_ENTRY *Node; 66 UINT32 Index; 67 68 Record = NULL; 69 CurrentTpl = gBS->RaiseTPL (EFI_TPL_HIGH_LEVEL); 70 71 if (!IsListEmpty (&mRecordsBuffer)) { 72 Node = GetFirstNode (&mRecordsBuffer); 73 RemoveEntryList (Node); 74 75 Record = _CR (Node, DATAHUB_STATUSCODE_RECORD, Node); 76 } else { 77 if (CurrentTpl > EFI_TPL_NOTIFY) { 78 gBS->RestoreTPL (CurrentTpl); 79 return NULL; 80 } 81 82 gBS->RestoreTPL (CurrentTpl); 83 84 gBS->AllocatePool (EfiBootServicesData, sizeof (DATAHUB_STATUSCODE_RECORD) * 16, (VOID **) &Record); 85 if (Record == NULL) { 86 return NULL; 87 } 88 EfiCommonLibZeroMem (Record, sizeof (DATAHUB_STATUSCODE_RECORD) * 16); 89 90 91 CurrentTpl = gBS->RaiseTPL (EFI_TPL_HIGH_LEVEL); 92 for (Index = 1; Index < 16; Index++) { 93 InsertTailList (&mRecordsBuffer, &Record[Index].Node); 94 } 95 } 96 97 Record->Signature = BS_DATA_HUB_STATUS_CODE_SIGNATURE; 98 InsertTailList (&mRecordsFifo, &Record->Node); 99 100 gBS->RestoreTPL (CurrentTpl); 101 102 return (DATA_HUB_STATUS_CODE_DATA_RECORD *) (Record->Data); 103 } 104 105 STATIC 106 DATA_HUB_STATUS_CODE_DATA_RECORD * 107 RetrieveRecord ( 108 VOID 109 ) 110 /*++ 111 112 Routine Description: 113 114 Retrieve one record from Records FIFO. The record would be removed from FIFO and 115 release to free record buffer. 116 117 Arguments: 118 119 None 120 121 Returns: 122 123 Point to record which is ready to be logged, or NULL if the FIFO of record is empty. 124 125 --*/ 126 { 127 DATA_HUB_STATUS_CODE_DATA_RECORD *RecordData; 128 DATAHUB_STATUSCODE_RECORD *Record; 129 EFI_LIST_ENTRY *Node; 130 EFI_TPL CurrentTpl; 131 132 RecordData = NULL; 133 134 CurrentTpl = gBS->RaiseTPL (EFI_TPL_HIGH_LEVEL); 135 136 if (!IsListEmpty (&mRecordsFifo)) { 137 Node = GetFirstNode (&mRecordsFifo); 138 Record = CR (Node, DATAHUB_STATUSCODE_RECORD, Node, BS_DATA_HUB_STATUS_CODE_SIGNATURE); 139 140 RemoveEntryList (&Record->Node); 141 InsertTailList (&mRecordsBuffer, &Record->Node); 142 Record->Signature = 0; 143 RecordData = (DATA_HUB_STATUS_CODE_DATA_RECORD *) Record->Data; 144 } 145 146 gBS->RestoreTPL (CurrentTpl); 147 148 return RecordData; 149 } 150 151 EFI_STATUS 152 EFIAPI 153 BsDataHubReportStatusCode ( 154 IN EFI_STATUS_CODE_TYPE CodeType, 155 IN EFI_STATUS_CODE_VALUE Value, 156 IN UINT32 Instance, 157 IN EFI_GUID * CallerId, 158 IN EFI_STATUS_CODE_DATA * Data OPTIONAL 159 ) 160 /*++ 161 162 Routine Description: 163 164 Boot service report status code listener. This function logs the status code 165 into the data hub. 166 167 Arguments: 168 169 Same as gRT->ReportStatusCode (See Tiano Runtime Specification) 170 171 Returns: 172 173 None 174 175 --*/ 176 { 177 DATA_HUB_STATUS_CODE_DATA_RECORD *Record; 178 UINT32 ErrorLevel; 179 VA_LIST Marker; 180 CHAR8 *Format; 181 CHAR16 FormatBuffer[BYTES_PER_RECORD]; 182 UINTN Index; 183 184 // 185 // See whether in runtime phase or not. 186 // 187 if (EfiAtRuntime ()) { 188 // 189 // For now all we do is post code at runtime 190 // 191 return EFI_SUCCESS; 192 } 193 194 // 195 // Discard new DataHubRecord caused by DataHub->LogData() 196 // 197 if (mEventHandlerActive) { 198 return EFI_SUCCESS; 199 } 200 201 Record = AcquireRecordBuffer (); 202 if (Record == NULL) { 203 // 204 // There are no empty record buffer in private buffers 205 // 206 return EFI_OUT_OF_RESOURCES; 207 } 208 // 209 // Construct Data Hub Extended Data 210 // 211 Record->CodeType = CodeType; 212 Record->Value = Value; 213 Record->Instance = Instance; 214 215 if (CallerId != NULL) { 216 EfiCopyMem (&Record->CallerId, CallerId, sizeof (EFI_GUID)); 217 } 218 219 if (Data != NULL) { 220 if (ReportStatusCodeExtractDebugInfo (Data, &ErrorLevel, &Marker, &Format)) { 221 // 222 // Convert Ascii Format string to Unicode. 223 // 224 for (Index = 0; Format[Index] != '\0' && Index < (BYTES_PER_RECORD - 1); Index += 1) { 225 FormatBuffer[Index] = (CHAR16) Format[Index]; 226 } 227 228 FormatBuffer[Index] = L'\0'; 229 230 // 231 // Put processed string into the buffer 232 // 233 Index = VSPrint ( 234 (CHAR16 *) (Record + 1), 235 BYTES_PER_RECORD - (sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD)), 236 FormatBuffer, 237 Marker 238 ); 239 240 EfiCopyMem (&Record->Data.Type, &gEfiStatusCodeDataTypeDebugGuid, sizeof (EFI_GUID)); 241 Record->Data.HeaderSize = Data->HeaderSize; 242 Record->Data.Size = (UINT16) (Index * sizeof (CHAR16)); 243 } else { 244 // 245 // Copy status code data header 246 // 247 EfiCopyMem (&Record->Data, Data, sizeof (EFI_STATUS_CODE_DATA)); 248 249 if (Data->Size > BYTES_PER_RECORD - sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD)) { 250 Record->Data.Size = (UINT16) (BYTES_PER_RECORD - sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD)); 251 } 252 EfiCopyMem ((VOID *) (Record + 1), Data + 1, Record->Data.Size); 253 } 254 } 255 256 gBS->SignalEvent (mLogDataHubEvent); 257 258 return EFI_SUCCESS; 259 } 260 261 VOID 262 EFIAPI 263 LogDataHubEventHandler ( 264 IN EFI_EVENT Event, 265 IN VOID *Context 266 ) 267 /*++ 268 269 Routine Description: 270 271 The Event handler which will be notified to log data in Data Hub. 272 273 Arguments: 274 275 Event - Instance of the EFI_EVENT to signal whenever data is 276 available to be logged in the system. 277 Context - Context of the event. 278 279 Returns: 280 281 None. 282 283 --*/ 284 { 285 DATA_HUB_STATUS_CODE_DATA_RECORD *Record; 286 UINT32 Size; 287 UINT64 DataRecordClass; 288 289 // 290 // Set global flag so we don't recurse if DataHub->LogData eventually causes new DataHubRecord 291 // 292 mEventHandlerActive = TRUE; 293 294 // 295 // Log DataRecord in Data Hub. 296 // Journal records fifo to find all record entry. 297 // 298 while (1) { 299 Record = RetrieveRecord (); 300 if (Record == NULL) { 301 break; 302 } 303 // 304 // Add in the size of the header we added. 305 // 306 Size = sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD) + (UINT32) Record->Data.Size; 307 308 if ((Record->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE) { 309 DataRecordClass = EFI_DATA_RECORD_CLASS_PROGRESS_CODE; 310 } else if ((Record->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_ERROR_CODE) { 311 DataRecordClass = EFI_DATA_RECORD_CLASS_ERROR; 312 } else if ((Record->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_DEBUG_CODE) { 313 DataRecordClass = EFI_DATA_RECORD_CLASS_DEBUG; 314 } else { 315 // 316 // Should never get here. 317 // 318 DataRecordClass = EFI_DATA_RECORD_CLASS_DEBUG | 319 EFI_DATA_RECORD_CLASS_ERROR | 320 EFI_DATA_RECORD_CLASS_DATA | 321 EFI_DATA_RECORD_CLASS_PROGRESS_CODE; 322 } 323 324 // 325 // Log DataRecord in Data Hub 326 // 327 328 mDataHubProtocol->LogData ( 329 mDataHubProtocol, 330 &gEfiStatusCodeGuid, 331 &gEfiStatusCodeRuntimeProtocolGuid, 332 DataRecordClass, 333 Record, 334 Size 335 ); 336 337 } 338 339 mEventHandlerActive = FALSE; 340 341 } 342 343 EFI_STATUS 344 EFIAPI 345 BsDataHubInitializeStatusCode ( 346 IN EFI_HANDLE ImageHandle, 347 IN EFI_SYSTEM_TABLE *SystemTable 348 ) 349 /*++ 350 351 Routine Description: 352 353 Install a data hub listener. 354 355 Arguments: 356 357 (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT) 358 359 Returns: 360 361 EFI_SUCCESS - Logging Hub protocol installed 362 Other - No protocol installed, unload driver. 363 364 --*/ 365 { 366 EFI_STATUS Status; 367 368 Status = gBS->LocateProtocol ( 369 &gEfiDataHubProtocolGuid, 370 NULL, 371 (VOID **) &mDataHubProtocol 372 ); 373 ASSERT_EFI_ERROR (Status); 374 375 // 376 // Create a Notify Event to log data in Data Hub 377 // 378 Status = gBS->CreateEvent ( 379 EFI_EVENT_NOTIFY_SIGNAL, 380 EFI_TPL_CALLBACK, 381 LogDataHubEventHandler, 382 NULL, 383 &mLogDataHubEvent 384 ); 385 386 ASSERT_EFI_ERROR (Status); 387 388 return EFI_SUCCESS; 389 } 390 391 392