1 /** @file 2 Debug Agent library implementition. 3 4 Copyright (c) 2010 - 2016, 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 "SmmDebugAgentLib.h" 16 17 DEBUG_AGENT_MAILBOX *mMailboxPointer = NULL; 18 DEBUG_AGENT_MAILBOX mLocalMailbox; 19 UINTN mSavedDebugRegisters[6]; 20 IA32_IDT_GATE_DESCRIPTOR mIdtEntryTable[33]; 21 BOOLEAN mSkipBreakpoint = FALSE; 22 BOOLEAN mSmmDebugIdtInitFlag = FALSE; 23 24 CHAR8 mWarningMsgIgnoreSmmEntryBreak[] = "Ignore smmentrybreak setting for SMI issued during DXE debugging!\r\n"; 25 26 /** 27 Check if debug agent support multi-processor. 28 29 @retval TRUE Multi-processor is supported. 30 @retval FALSE Multi-processor is not supported. 31 32 **/ 33 BOOLEAN 34 MultiProcessorDebugSupport ( 35 VOID 36 ) 37 { 38 return FALSE; 39 } 40 41 /** 42 Read the Attach/Break-in symbols from the debug port. 43 44 @param[in] Handle Pointer to Debug Port handle. 45 @param[out] BreakSymbol Returned break symbol. 46 47 @retval EFI_SUCCESS Read the symbol in BreakSymbol. 48 @retval EFI_NOT_FOUND No read the break symbol. 49 50 **/ 51 EFI_STATUS 52 DebugReadBreakSymbol ( 53 IN DEBUG_PORT_HANDLE Handle, 54 OUT UINT8 *BreakSymbol 55 ) 56 { 57 // 58 // Smm instance has no debug timer to poll break symbol. 59 // 60 return EFI_NOT_FOUND; 61 } 62 63 /** 64 Get the pointer to Mailbox from the GUIDed HOB. 65 66 @return Pointer to Mailbox. 67 68 **/ 69 DEBUG_AGENT_MAILBOX * 70 GetMailboxFromHob ( 71 VOID 72 ) 73 { 74 EFI_HOB_GUID_TYPE *GuidHob; 75 UINT64 *MailboxLocation; 76 DEBUG_AGENT_MAILBOX *Mailbox; 77 78 GuidHob = GetFirstGuidHob (&gEfiDebugAgentGuid); 79 if (GuidHob == NULL) { 80 return NULL; 81 } 82 MailboxLocation = (UINT64 *) (GET_GUID_HOB_DATA(GuidHob)); 83 Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); 84 VerifyMailboxChecksum (Mailbox); 85 86 return Mailbox; 87 } 88 89 /** 90 Get Debug Agent Mailbox pointer. 91 92 @return Mailbox pointer. 93 94 **/ 95 DEBUG_AGENT_MAILBOX * 96 GetMailboxPointer ( 97 VOID 98 ) 99 { 100 VerifyMailboxChecksum (mMailboxPointer); 101 return mMailboxPointer; 102 } 103 104 /** 105 Get debug port handle. 106 107 @return Debug port handle. 108 109 **/ 110 DEBUG_PORT_HANDLE 111 GetDebugPortHandle ( 112 VOID 113 ) 114 { 115 return (DEBUG_PORT_HANDLE) (UINTN)(GetMailboxPointer()->DebugPortHandle); 116 } 117 118 /** 119 Store debug register when SMI exit. 120 121 **/ 122 VOID 123 SaveDebugRegister ( 124 VOID 125 ) 126 { 127 mSavedDebugRegisters[0] = AsmReadDr0 (); 128 mSavedDebugRegisters[1] = AsmReadDr1 (); 129 mSavedDebugRegisters[2] = AsmReadDr2 (); 130 mSavedDebugRegisters[3] = AsmReadDr3 (); 131 mSavedDebugRegisters[4] = AsmReadDr6 (); 132 mSavedDebugRegisters[5] = AsmReadDr7 (); 133 } 134 135 /** 136 Restore debug register when SMI exit. 137 138 **/ 139 VOID 140 RestoreDebugRegister ( 141 VOID 142 ) 143 { 144 AsmWriteDr7 (0); 145 AsmWriteDr0 (mSavedDebugRegisters[0]); 146 AsmWriteDr1 (mSavedDebugRegisters[1]); 147 AsmWriteDr2 (mSavedDebugRegisters[2]); 148 AsmWriteDr3 (mSavedDebugRegisters[3]); 149 AsmWriteDr6 (mSavedDebugRegisters[4]); 150 AsmWriteDr7 (mSavedDebugRegisters[5]); 151 } 152 153 /** 154 Initialize debug agent. 155 156 This function is used to set up debug enviroment for source level debug 157 in SMM code. 158 159 If InitFlag is DEBUG_AGENT_INIT_SMM, it will overirde IDT table entries 160 and initialize debug port. It will get debug agent Mailbox from GUIDed HOB, 161 it it exists, debug agent wiil copied it into the local Mailbox in SMM space. 162 it will overirde IDT table entries and initialize debug port. Context will be 163 NULL. 164 If InitFlag is DEBUG_AGENT_INIT_ENTER_SMI, debug agent will save Debug 165 Registers and get local Mailbox in SMM space. Context will be NULL. 166 If InitFlag is DEBUG_AGENT_INIT_EXIT_SMI, debug agent will restore Debug 167 Registers. Context will be NULL. 168 169 @param[in] InitFlag Init flag is used to decide initialize process. 170 @param[in] Context Context needed according to InitFlag. 171 @param[in] Function Continue function called by debug agent library; it was 172 optional. 173 174 **/ 175 VOID 176 EFIAPI 177 InitializeDebugAgent ( 178 IN UINT32 InitFlag, 179 IN VOID *Context, OPTIONAL 180 IN DEBUG_AGENT_CONTINUE Function OPTIONAL 181 ) 182 { 183 EFI_STATUS Status; 184 UINT64 DebugPortHandle; 185 IA32_IDT_GATE_DESCRIPTOR IdtEntry[33]; 186 IA32_DESCRIPTOR IdtDescriptor; 187 IA32_DESCRIPTOR *Ia32Idtr; 188 IA32_IDT_ENTRY *Ia32IdtEntry; 189 IA32_DESCRIPTOR Idtr; 190 UINT16 IdtEntryCount; 191 DEBUG_AGENT_MAILBOX *Mailbox; 192 UINT64 *MailboxLocation; 193 UINT32 DebugTimerFrequency; 194 BOOLEAN PeriodicMode; 195 UINTN TimerCycle; 196 197 switch (InitFlag) { 198 case DEBUG_AGENT_INIT_SMM: 199 // 200 // Install configuration table for persisted vector handoff info 201 // 202 Status = gSmst->SmmInstallConfigurationTable ( 203 gSmst, 204 &gEfiVectorHandoffTableGuid, 205 (VOID *) &mVectorHandoffInfoDebugAgent[0], 206 sizeof (EFI_VECTOR_HANDOFF_INFO) * mVectorHandoffInfoCount 207 ); 208 if (EFI_ERROR (Status)) { 209 DEBUG ((EFI_D_ERROR, "DebugAgent: Cannot install configuration table for persisted vector handoff info!\n")); 210 CpuDeadLoop (); 211 } 212 // 213 // Check if Debug Agent initialized in DXE phase 214 // 215 Status = EfiGetSystemConfigurationTable (&gEfiDebugAgentGuid, (VOID **) &Mailbox); 216 if (Status == EFI_SUCCESS && Mailbox != NULL) { 217 VerifyMailboxChecksum (Mailbox); 218 mMailboxPointer = Mailbox; 219 break; 220 } 221 // 222 // Check if Debug Agent initialized in SEC/PEI phase 223 // 224 Mailbox = GetMailboxFromHob (); 225 if (Mailbox != NULL) { 226 mMailboxPointer = Mailbox; 227 break; 228 } 229 // 230 // Debug Agent was not initialized before, use the local mailbox. 231 // 232 ZeroMem (&mLocalMailbox, sizeof (DEBUG_AGENT_MAILBOX)); 233 Mailbox = &mLocalMailbox; 234 // 235 // Save original IDT entries 236 // 237 AsmReadIdtr (&IdtDescriptor); 238 CopyMem (&IdtEntry, (VOID *)IdtDescriptor.Base, 33 * sizeof(IA32_IDT_GATE_DESCRIPTOR)); 239 // 240 // Initialized Debug Agent 241 // 242 InitializeDebugIdt (); 243 // 244 // Initialize Debug Timer hardware and save its frequency 245 // 246 InitializeDebugTimer (&DebugTimerFrequency, TRUE); 247 UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); 248 249 DebugPortHandle = (UINT64) (UINTN)DebugPortInitialize ((DEBUG_PORT_HANDLE) (UINTN)Mailbox->DebugPortHandle, NULL); 250 UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle); 251 mMailboxPointer = Mailbox; 252 // 253 // Trigger one software interrupt to inform HOST 254 // 255 TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE); 256 257 SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1); 258 // 259 // Memory has been ready 260 // 261 if (IsHostAttached ()) { 262 // 263 // Trigger one software interrupt to inform HOST 264 // 265 TriggerSoftInterrupt (MEMORY_READY_SIGNATURE); 266 } 267 // 268 // Find and report PE/COFF image info to HOST 269 // 270 FindAndReportModuleImageInfo (SIZE_4KB); 271 // 272 // Restore saved IDT entries 273 // 274 CopyMem ((VOID *)IdtDescriptor.Base, &IdtEntry, 33 * sizeof(IA32_IDT_GATE_DESCRIPTOR)); 275 276 break; 277 278 case DEBUG_AGENT_INIT_ENTER_SMI: 279 SaveDebugRegister (); 280 if (!mSmmDebugIdtInitFlag) { 281 // 282 // We only need to initialize Debug IDT table at first SMI entry 283 // after SMM relocation. 284 // 285 InitializeDebugIdt (); 286 mSmmDebugIdtInitFlag = TRUE; 287 } 288 // 289 // Check if CPU APIC Timer is working, otherwise initialize it. 290 // 291 InitializeLocalApicSoftwareEnable (TRUE); 292 GetApicTimerState (NULL, &PeriodicMode, NULL); 293 TimerCycle = GetApicTimerInitCount (); 294 if (!PeriodicMode || TimerCycle == 0) { 295 InitializeDebugTimer (NULL, FALSE); 296 } 297 Mailbox = GetMailboxPointer (); 298 if (GetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS) == 1) { 299 // 300 // If Debug Agent has been communicaton state with HOST, we need skip 301 // any break points set in SMM, set Skip Breakpoint flag 302 // 303 mSkipBreakpoint = TRUE; 304 } 305 if (GetDebugFlag (DEBUG_AGENT_FLAG_BREAK_ON_NEXT_SMI) == 1) { 306 if (mSkipBreakpoint) { 307 // 308 // Print warning message if ignore smm entry break 309 // 310 DebugPortWriteBuffer ((DEBUG_PORT_HANDLE) (UINTN)Mailbox->DebugPortHandle, 311 (UINT8 *)mWarningMsgIgnoreSmmEntryBreak, 312 AsciiStrLen (mWarningMsgIgnoreSmmEntryBreak) 313 ); 314 } else { 315 // 316 // If SMM entry break is set, SMM code will be break at here. 317 // 318 CpuBreakpoint (); 319 } 320 } 321 break; 322 323 case DEBUG_AGENT_INIT_EXIT_SMI: 324 Mailbox = GetMailboxPointer (); 325 // 326 // Clear Skip Breakpoint flag 327 // 328 mSkipBreakpoint = FALSE; 329 RestoreDebugRegister (); 330 break; 331 332 case DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64: 333 if (Context == NULL) { 334 DEBUG ((EFI_D_ERROR, "DebugAgent: Input parameter Context cannot be NULL!\n")); 335 CpuDeadLoop (); 336 } else { 337 Ia32Idtr = (IA32_DESCRIPTOR *) Context; 338 Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base); 339 MailboxLocation = (UINT64 *) (UINTN) (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow + 340 (UINT32) (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16)); 341 mMailboxPointer = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); 342 VerifyMailboxChecksum (mMailboxPointer); 343 // 344 // Get original IDT address and size. 345 // 346 AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr); 347 IdtEntryCount = (UINT16) ((Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)); 348 if (IdtEntryCount < 33) { 349 Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33 - 1); 350 Idtr.Base = (UINTN) &mIdtEntryTable; 351 ZeroMem (&mIdtEntryTable, Idtr.Limit + 1); 352 AsmWriteIdtr ((IA32_DESCRIPTOR *) &Idtr); 353 } 354 355 InitializeDebugIdt (); 356 // 357 // Initialize Debug Timer hardware and save its frequency 358 // 359 InitializeDebugTimer (&DebugTimerFrequency, TRUE); 360 UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); 361 // 362 // Enable Debug Timer interrupt and CPU interrupt 363 // 364 SaveAndSetDebugTimerInterrupt (TRUE); 365 EnableInterrupts (); 366 367 FindAndReportModuleImageInfo (SIZE_4KB); 368 } 369 break; 370 371 default: 372 // 373 // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this 374 // Debug Agent library instance. 375 // 376 DEBUG ((EFI_D_ERROR, "Debug Agent: The InitFlag value is not allowed!\n")); 377 CpuDeadLoop (); 378 break; 379 } 380 } 381 382