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