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
MultiProcessorDebugSupport(VOID)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
DebugReadBreakSymbol(IN DEBUG_PORT_HANDLE Handle,OUT UINT8 * BreakSymbol)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 *
GetMailboxFromHob(VOID)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 *
GetMailboxPointer(VOID)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
GetDebugPortHandle(VOID)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
SaveDebugRegister(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
RestoreDebugRegister(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
InitializeDebugAgent(IN UINT32 InitFlag,IN VOID * Context,OPTIONAL IN DEBUG_AGENT_CONTINUE Function OPTIONAL)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