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