1 /** @file 2 Debug Agent library implementition for Dxe Core and Dxr modules. 3 4 Copyright (c) 2010 - 2015, 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 "DxeDebugAgentLib.h" 16 17 DEBUG_AGENT_MAILBOX mMailbox; 18 DEBUG_AGENT_MAILBOX *mMailboxPointer = NULL; 19 IA32_IDT_GATE_DESCRIPTOR mIdtEntryTable[33]; 20 BOOLEAN mDxeCoreFlag = FALSE; 21 BOOLEAN mMultiProcessorDebugSupport = FALSE; 22 VOID *mSavedIdtTable = NULL; 23 UINTN mSaveIdtTableSize = 0; 24 BOOLEAN mDebugAgentInitialized = FALSE; 25 BOOLEAN mSkipBreakpoint = FALSE; 26 27 /** 28 Check if debug agent support multi-processor. 29 30 @retval TRUE Multi-processor is supported. 31 @retval FALSE Multi-processor is not supported. 32 33 **/ 34 BOOLEAN 35 MultiProcessorDebugSupport ( 36 VOID 37 ) 38 { 39 return mMultiProcessorDebugSupport; 40 } 41 42 /** 43 Internal constructor worker function. 44 45 It will register one callback function on EFI PCD Protocol. 46 It will allocate the NVS memory to store Mailbox and install configuration table 47 in system table to store its pointer. 48 49 **/ 50 VOID 51 InternalConstructorWorker ( 52 VOID 53 ) 54 { 55 EFI_STATUS Status; 56 EFI_PHYSICAL_ADDRESS Address; 57 BOOLEAN DebugTimerInterruptState; 58 DEBUG_AGENT_MAILBOX *Mailbox; 59 DEBUG_AGENT_MAILBOX *NewMailbox; 60 EFI_HOB_GUID_TYPE *GuidHob; 61 EFI_VECTOR_HANDOFF_INFO *VectorHandoffInfo; 62 63 // 64 // Check persisted vector handoff info 65 // 66 Status = EFI_SUCCESS; 67 GuidHob = GetFirstGuidHob (&gEfiVectorHandoffInfoPpiGuid); 68 if (GuidHob != NULL && !mDxeCoreFlag) { 69 // 70 // Check if configuration table is installed or not if GUIDed HOB existed, 71 // only when Debug Agent is not linked by DXE Core 72 // 73 Status = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **) &VectorHandoffInfo); 74 } 75 if (GuidHob == NULL || Status != EFI_SUCCESS) { 76 // 77 // Install configuration table for persisted vector handoff info if GUIDed HOB cannot be found or 78 // configuration table does not exist 79 // 80 Status = gBS->InstallConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID *) &mVectorHandoffInfoDebugAgent[0]); 81 if (EFI_ERROR (Status)) { 82 DEBUG ((EFI_D_ERROR, "DebugAgent: Cannot install configuration table for persisted vector handoff info!\n")); 83 CpuDeadLoop (); 84 } 85 } 86 87 // 88 // Install EFI Serial IO protocol on debug port 89 // 90 InstallSerialIo (); 91 92 Address = 0; 93 Status = gBS->AllocatePages ( 94 AllocateAnyPages, 95 EfiACPIMemoryNVS, 96 EFI_SIZE_TO_PAGES (sizeof(DEBUG_AGENT_MAILBOX) + PcdGet16(PcdDebugPortHandleBufferSize)), 97 &Address 98 ); 99 if (EFI_ERROR (Status)) { 100 DEBUG ((EFI_D_ERROR, "DebugAgent: Cannot install configuration table for mailbox!\n")); 101 CpuDeadLoop (); 102 } 103 104 DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (FALSE); 105 106 NewMailbox = (DEBUG_AGENT_MAILBOX *) (UINTN) Address; 107 // 108 // Copy Mailbox and Debug Port Handle buffer to new location in ACPI NVS memory, because original Mailbox 109 // and Debug Port Handle buffer may be free at runtime, SMM debug agent needs to access them 110 // 111 Mailbox = GetMailboxPointer (); 112 CopyMem (NewMailbox, Mailbox, sizeof (DEBUG_AGENT_MAILBOX)); 113 CopyMem (NewMailbox + 1, (VOID *)(UINTN)Mailbox->DebugPortHandle, PcdGet16(PcdDebugPortHandleBufferSize)); 114 // 115 // Update Debug Port Handle in new Mailbox 116 // 117 UpdateMailboxContent (NewMailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, (UINT64)(UINTN)(NewMailbox + 1)); 118 mMailboxPointer = NewMailbox; 119 120 DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (DebugTimerInterruptState); 121 122 Status = gBS->InstallConfigurationTable (&gEfiDebugAgentGuid, (VOID *) mMailboxPointer); 123 if (EFI_ERROR (Status)) { 124 DEBUG ((EFI_D_ERROR, "DebugAgent: Failed to install configuration for mailbox!\n")); 125 CpuDeadLoop (); 126 } 127 } 128 129 /** 130 Debug Agent constructor function. 131 132 @param[in] ImageHandle The firmware allocated handle for the EFI image. 133 @param[in] SystemTable A pointer to the EFI System Table. 134 135 @retval RETURN_SUCCESS When this function completed. 136 137 **/ 138 RETURN_STATUS 139 EFIAPI 140 DxeDebugAgentLibConstructor ( 141 IN EFI_HANDLE ImageHandle, 142 IN EFI_SYSTEM_TABLE *SystemTable 143 ) 144 { 145 if (mDxeCoreFlag) { 146 // 147 // Invoke internal constructor function only when DXE core links this library instance 148 // 149 InternalConstructorWorker (); 150 } 151 152 return RETURN_SUCCESS; 153 } 154 155 /** 156 Get the pointer to Mailbox from the configuration table. 157 158 @return Pointer to Mailbox. 159 160 **/ 161 DEBUG_AGENT_MAILBOX * 162 GetMailboxFromConfigurationTable ( 163 VOID 164 ) 165 { 166 EFI_STATUS Status; 167 DEBUG_AGENT_MAILBOX *Mailbox; 168 169 Status = EfiGetSystemConfigurationTable (&gEfiDebugAgentGuid, (VOID **) &Mailbox); 170 if (Status == EFI_SUCCESS && Mailbox != NULL) { 171 VerifyMailboxChecksum (Mailbox); 172 return Mailbox; 173 } else { 174 return NULL; 175 } 176 } 177 178 /** 179 Get the pointer to Mailbox from the GUIDed HOB. 180 181 @param[in] HobStart The starting HOB pointer to search from. 182 183 @return Pointer to Mailbox. 184 185 **/ 186 DEBUG_AGENT_MAILBOX * 187 GetMailboxFromHob ( 188 IN VOID *HobStart 189 ) 190 { 191 EFI_HOB_GUID_TYPE *GuidHob; 192 UINT64 *MailboxLocation; 193 DEBUG_AGENT_MAILBOX *Mailbox; 194 195 GuidHob = GetNextGuidHob (&gEfiDebugAgentGuid, HobStart); 196 if (GuidHob == NULL) { 197 return NULL; 198 } 199 MailboxLocation = (UINT64 *) (GET_GUID_HOB_DATA(GuidHob)); 200 Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); 201 VerifyMailboxChecksum (Mailbox); 202 203 return Mailbox; 204 } 205 206 /** 207 Get Debug Agent Mailbox pointer. 208 209 @return Mailbox pointer. 210 211 **/ 212 DEBUG_AGENT_MAILBOX * 213 GetMailboxPointer ( 214 VOID 215 ) 216 { 217 AcquireMpSpinLock (&mDebugMpContext.MailboxSpinLock); 218 VerifyMailboxChecksum (mMailboxPointer); 219 ReleaseMpSpinLock (&mDebugMpContext.MailboxSpinLock); 220 return mMailboxPointer; 221 } 222 223 /** 224 Get debug port handle. 225 226 @return Debug port handle. 227 228 **/ 229 DEBUG_PORT_HANDLE 230 GetDebugPortHandle ( 231 VOID 232 ) 233 { 234 return (DEBUG_PORT_HANDLE) (UINTN)(GetMailboxPointer ()->DebugPortHandle); 235 } 236 237 /** 238 Worker function to set up Debug Agent environment. 239 240 This function will set up IDT table and initialize the IDT entries and 241 initialize CPU LOCAL APIC timer. 242 It also tries to connect HOST if Debug Agent was not initialized before. 243 244 @param[in] Mailbox Pointer to Mailbox. 245 246 **/ 247 VOID 248 SetupDebugAgentEnvironment ( 249 IN DEBUG_AGENT_MAILBOX *Mailbox 250 ) 251 { 252 IA32_DESCRIPTOR Idtr; 253 UINT16 IdtEntryCount; 254 UINT64 DebugPortHandle; 255 UINT32 DebugTimerFrequency; 256 257 if (mMultiProcessorDebugSupport) { 258 InitializeSpinLock (&mDebugMpContext.MpContextSpinLock); 259 InitializeSpinLock (&mDebugMpContext.DebugPortSpinLock); 260 InitializeSpinLock (&mDebugMpContext.MailboxSpinLock); 261 // 262 // Clear Break CPU index value 263 // 264 mDebugMpContext.BreakAtCpuIndex = (UINT32) -1; 265 } 266 267 // 268 // Get original IDT address and size. 269 // 270 AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr); 271 IdtEntryCount = (UINT16) ((Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)); 272 if (IdtEntryCount < 33) { 273 ZeroMem (&mIdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33); 274 // 275 // Copy original IDT table into new one 276 // 277 CopyMem (&mIdtEntryTable, (VOID *) Idtr.Base, Idtr.Limit + 1); 278 // 279 // Load new IDT table 280 // 281 Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33 - 1); 282 Idtr.Base = (UINTN) &mIdtEntryTable; 283 AsmWriteIdtr ((IA32_DESCRIPTOR *) &Idtr); 284 } 285 286 // 287 // Initialize the IDT table entries to support source level debug. 288 // 289 InitializeDebugIdt (); 290 291 // 292 // If mMailboxPointer is not set before, set it 293 // 294 if (mMailboxPointer == NULL) { 295 if (Mailbox != NULL) { 296 // 297 // If Mailbox exists, copy it into one global variable 298 // 299 CopyMem (&mMailbox, Mailbox, sizeof (DEBUG_AGENT_MAILBOX)); 300 } else { 301 ZeroMem (&mMailbox, sizeof (DEBUG_AGENT_MAILBOX)); 302 } 303 mMailboxPointer = &mMailbox; 304 } 305 306 // 307 // Initialize Debug Timer hardware and save its initial count and frequency 308 // 309 mDebugMpContext.DebugTimerInitCount = InitializeDebugTimer (&DebugTimerFrequency, TRUE); 310 UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); 311 // 312 // Initialize debug communication port 313 // 314 DebugPortHandle = (UINT64) (UINTN)DebugPortInitialize ((VOID *)(UINTN)mMailboxPointer->DebugPortHandle, NULL); 315 UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle); 316 317 if (Mailbox == NULL) { 318 // 319 // Trigger one software interrupt to inform HOST 320 // 321 TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE); 322 SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1); 323 // 324 // Memory has been ready 325 // 326 if (IsHostAttached ()) { 327 // 328 // Trigger one software interrupt to inform HOST 329 // 330 TriggerSoftInterrupt (MEMORY_READY_SIGNATURE); 331 } 332 } 333 } 334 335 336 /** 337 Initialize debug agent. 338 339 This function is used to set up debug environment for DXE phase. 340 341 If this function is called by DXE Core, Context must be the pointer 342 to HOB list which will be used to get GUIDed HOB. It will enable 343 interrupt to support break-in feature. 344 If this function is called by DXE module, Context must be NULL. It 345 will enable interrupt to support break-in feature. 346 347 @param[in] InitFlag Init flag is used to decide initialize process. 348 @param[in] Context Context needed according to InitFlag. 349 @param[in] Function Continue function called by debug agent library; it was 350 optional. 351 352 **/ 353 VOID 354 EFIAPI 355 InitializeDebugAgent ( 356 IN UINT32 InitFlag, 357 IN VOID *Context, OPTIONAL 358 IN DEBUG_AGENT_CONTINUE Function OPTIONAL 359 ) 360 { 361 UINT64 *MailboxLocation; 362 DEBUG_AGENT_MAILBOX *Mailbox; 363 BOOLEAN InterruptStatus; 364 VOID *HobList; 365 IA32_DESCRIPTOR IdtDescriptor; 366 IA32_DESCRIPTOR *Ia32Idtr; 367 IA32_IDT_ENTRY *Ia32IdtEntry; 368 BOOLEAN PeriodicMode; 369 UINTN TimerCycle; 370 371 if (InitFlag == DEBUG_AGENT_INIT_DXE_AP) { 372 // 373 // Check if CPU APIC Timer is working, otherwise initialize it. 374 // 375 InitializeLocalApicSoftwareEnable (TRUE); 376 GetApicTimerState (NULL, &PeriodicMode, NULL); 377 TimerCycle = GetApicTimerInitCount (); 378 if (!PeriodicMode || TimerCycle == 0) { 379 InitializeDebugTimer (NULL, FALSE); 380 } 381 // 382 // Invoked by AP, enable interrupt to let AP could receive IPI from other processors 383 // 384 EnableInterrupts (); 385 return ; 386 } 387 388 // 389 // Disable Debug Timer interrupt 390 // 391 SaveAndSetDebugTimerInterrupt (FALSE); 392 // 393 // Save and disable original interrupt status 394 // 395 InterruptStatus = SaveAndDisableInterrupts (); 396 397 // 398 // Try to get mailbox firstly 399 // 400 HobList = NULL; 401 Mailbox = NULL; 402 MailboxLocation = NULL; 403 404 switch (InitFlag) { 405 406 case DEBUG_AGENT_INIT_DXE_LOAD: 407 // 408 // Check if Debug Agent has been initialized before 409 // 410 if (IsDebugAgentInitialzed ()) { 411 DEBUG ((EFI_D_INFO, "Debug Agent: The former agent will be overwritten by the new one!\n")); 412 } 413 414 mMultiProcessorDebugSupport = TRUE; 415 // 416 // Save original IDT table 417 // 418 AsmReadIdtr (&IdtDescriptor); 419 mSaveIdtTableSize = IdtDescriptor.Limit + 1; 420 mSavedIdtTable = AllocateCopyPool (mSaveIdtTableSize, (VOID *) IdtDescriptor.Base); 421 // 422 // Check if Debug Agent initialized in DXE phase 423 // 424 Mailbox = GetMailboxFromConfigurationTable (); 425 if (Mailbox == NULL) { 426 // 427 // Try to get mailbox from GUIDed HOB build in PEI 428 // 429 HobList = GetHobList (); 430 Mailbox = GetMailboxFromHob (HobList); 431 } 432 // 433 // Set up Debug Agent Environment and try to connect HOST if required 434 // 435 SetupDebugAgentEnvironment (Mailbox); 436 // 437 // For DEBUG_AGENT_INIT_S3, needn't to install configuration table and EFI Serial IO protocol 438 // For DEBUG_AGENT_INIT_DXE_CORE, InternalConstructorWorker() will invoked in Constructor() 439 // 440 InternalConstructorWorker (); 441 // 442 // Enable Debug Timer interrupt 443 // 444 SaveAndSetDebugTimerInterrupt (TRUE); 445 // 446 // Enable interrupt to receive Debug Timer interrupt 447 // 448 EnableInterrupts (); 449 450 mDebugAgentInitialized = TRUE; 451 FindAndReportModuleImageInfo (SIZE_4KB); 452 453 *(EFI_STATUS *)Context = EFI_SUCCESS; 454 455 break; 456 457 case DEBUG_AGENT_INIT_DXE_UNLOAD: 458 if (mDebugAgentInitialized) { 459 if (IsHostAttached ()) { 460 *(EFI_STATUS *)Context = EFI_ACCESS_DENIED; 461 // 462 // Enable Debug Timer interrupt again 463 // 464 SaveAndSetDebugTimerInterrupt (TRUE); 465 } else { 466 // 467 // Restore original IDT table 468 // 469 AsmReadIdtr (&IdtDescriptor); 470 IdtDescriptor.Limit = (UINT16) (mSaveIdtTableSize - 1); 471 CopyMem ((VOID *) IdtDescriptor.Base, mSavedIdtTable, mSaveIdtTableSize); 472 AsmWriteIdtr (&IdtDescriptor); 473 FreePool (mSavedIdtTable); 474 mDebugAgentInitialized = FALSE; 475 *(EFI_STATUS *)Context = EFI_SUCCESS; 476 } 477 } else { 478 *(EFI_STATUS *)Context = EFI_NOT_STARTED; 479 } 480 481 // 482 // Restore interrupt state. 483 // 484 SetInterruptState (InterruptStatus); 485 break; 486 487 case DEBUG_AGENT_INIT_DXE_CORE: 488 mDxeCoreFlag = TRUE; 489 mMultiProcessorDebugSupport = TRUE; 490 // 491 // Try to get mailbox from GUIDed HOB build in PEI 492 // 493 HobList = Context; 494 Mailbox = GetMailboxFromHob (HobList); 495 // 496 // Set up Debug Agent Environment and try to connect HOST if required 497 // 498 SetupDebugAgentEnvironment (Mailbox); 499 // 500 // Enable Debug Timer interrupt 501 // 502 SaveAndSetDebugTimerInterrupt (TRUE); 503 // 504 // Enable interrupt to receive Debug Timer interrupt 505 // 506 EnableInterrupts (); 507 508 break; 509 510 case DEBUG_AGENT_INIT_S3: 511 512 if (Context != NULL) { 513 Ia32Idtr = (IA32_DESCRIPTOR *) Context; 514 Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base); 515 MailboxLocation = (UINT64 *) (UINTN) (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow + 516 (UINT32) (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16)); 517 Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); 518 VerifyMailboxChecksum (Mailbox); 519 } 520 // 521 // Save Mailbox pointer in global variable 522 // 523 mMailboxPointer = Mailbox; 524 // 525 // Set up Debug Agent Environment and try to connect HOST if required 526 // 527 SetupDebugAgentEnvironment (Mailbox); 528 // 529 // Disable interrupt 530 // 531 DisableInterrupts (); 532 FindAndReportModuleImageInfo (SIZE_4KB); 533 if (GetDebugFlag (DEBUG_AGENT_FLAG_BREAK_BOOT_SCRIPT) == 1) { 534 // 535 // If Boot Script entry break is set, code will be break at here. 536 // 537 CpuBreakpoint (); 538 } 539 break; 540 541 default: 542 // 543 // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this 544 // Debug Agent library instance. 545 // 546 DEBUG ((EFI_D_ERROR, "Debug Agent: The InitFlag value is not allowed!\n")); 547 CpuDeadLoop (); 548 break; 549 } 550 } 551