1 /** @file
2   Debug Agent library implementation 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