SmmDebugAgentLib.c revision df60fb4cc2ca896fcea9e37b06c276d569f1a6b8
1/** @file
2  Debug Agent library implementition.
3
4  Copyright (c) 2010 - 2015, 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
17DEBUG_AGENT_MAILBOX         *mMailboxPointer = NULL;
18DEBUG_AGENT_MAILBOX         mLocalMailbox;
19UINTN                       mSavedDebugRegisters[6];
20IA32_IDT_GATE_DESCRIPTOR    mIdtEntryTable[33];
21BOOLEAN                     mSkipBreakpoint = FALSE;
22
23CHAR8 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**/
32BOOLEAN
33MultiProcessorDebugSupport (
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**/
50EFI_STATUS
51DebugReadBreakSymbol (
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**/
68DEBUG_AGENT_MAILBOX *
69GetMailboxFromHob (
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**/
94DEBUG_AGENT_MAILBOX *
95GetMailboxPointer (
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**/
109DEBUG_PORT_HANDLE
110GetDebugPortHandle (
111  VOID
112  )
113{
114  return (DEBUG_PORT_HANDLE) (UINTN)(GetMailboxPointer()->DebugPortHandle);
115}
116
117/**
118  Store debug register when SMI exit.
119
120**/
121VOID
122SaveDebugRegister (
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**/
138VOID
139RestoreDebugRegister (
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 enviroment for source level debug
156  in SMM code.
157
158  If InitFlag is DEBUG_AGENT_INIT_SMM, it will overirde 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 overirde 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**/
174VOID
175EFIAPI
176InitializeDebugAgent (
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  BOOLEAN                       PeriodicMode;
194  UINTN                         TimerCycle;
195
196  switch (InitFlag) {
197  case DEBUG_AGENT_INIT_SMM:
198    //
199    // Install configuration table for persisted vector handoff info
200    //
201    Status = gSmst->SmmInstallConfigurationTable (
202                      gSmst,
203                      &gEfiVectorHandoffTableGuid,
204                      (VOID *) &mVectorHandoffInfoDebugAgent[0],
205                      sizeof (EFI_VECTOR_HANDOFF_INFO) * mVectorHandoffInfoCount
206                      );
207    if (EFI_ERROR (Status)) {
208      DEBUG ((EFI_D_ERROR, "DebugAgent: Cannot install configuration table for persisted vector handoff info!\n"));
209      CpuDeadLoop ();
210    }
211    //
212    // Check if Debug Agent initialized in DXE phase
213    //
214    Status = EfiGetSystemConfigurationTable (&gEfiDebugAgentGuid, (VOID **) &Mailbox);
215    if (Status == EFI_SUCCESS && Mailbox != NULL) {
216      VerifyMailboxChecksum (Mailbox);
217      mMailboxPointer = Mailbox;
218      break;
219    }
220    //
221    // Check if Debug Agent initialized in SEC/PEI phase
222    //
223    Mailbox = GetMailboxFromHob ();
224    if (Mailbox != NULL) {
225      mMailboxPointer = Mailbox;
226      break;
227    }
228    //
229    // Debug Agent was not initialized before, use the local mailbox.
230    //
231    ZeroMem (&mLocalMailbox, sizeof (DEBUG_AGENT_MAILBOX));
232    Mailbox = &mLocalMailbox;
233    //
234    // Save original IDT entries
235    //
236    AsmReadIdtr (&IdtDescriptor);
237    CopyMem (&IdtEntry, (VOID *)IdtDescriptor.Base, 33 * sizeof(IA32_IDT_GATE_DESCRIPTOR));
238    //
239    // Initialized Debug Agent
240    //
241    InitializeDebugIdt ();
242    //
243    // Initialize Debug Timer hardware and save its frequency
244    //
245    InitializeDebugTimer (&DebugTimerFrequency, TRUE);
246    UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency);
247
248    DebugPortHandle = (UINT64) (UINTN)DebugPortInitialize ((DEBUG_PORT_HANDLE) (UINTN)Mailbox->DebugPortHandle, NULL);
249    UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle);
250    mMailboxPointer = Mailbox;
251    //
252    // Trigger one software interrupt to inform HOST
253    //
254    TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE);
255
256    SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1);
257    //
258    // Memory has been ready
259    //
260    if (IsHostAttached ()) {
261      //
262      // Trigger one software interrupt to inform HOST
263      //
264      TriggerSoftInterrupt (MEMORY_READY_SIGNATURE);
265    }
266    //
267    // Find and report PE/COFF image info to HOST
268    //
269    FindAndReportModuleImageInfo (SIZE_4KB);
270    //
271    // Restore saved IDT entries
272    //
273    CopyMem ((VOID *)IdtDescriptor.Base, &IdtEntry, 33 * sizeof(IA32_IDT_GATE_DESCRIPTOR));
274
275    break;
276
277  case DEBUG_AGENT_INIT_ENTER_SMI:
278    SaveDebugRegister ();
279    InitializeDebugIdt ();
280    //
281    // Check if CPU APIC Timer is working, otherwise initialize it.
282    //
283    InitializeLocalApicSoftwareEnable (TRUE);
284    GetApicTimerState (NULL, &PeriodicMode, NULL);
285    TimerCycle = GetApicTimerInitCount ();
286    if (!PeriodicMode || TimerCycle == 0) {
287      InitializeDebugTimer (NULL, FALSE);
288    }
289    Mailbox = GetMailboxPointer ();
290    if (GetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS) == 1) {
291      //
292      // If Debug Agent has been communicaton state with HOST, we need skip
293      // any break points set in SMM, set Skip Breakpoint flag
294      //
295      mSkipBreakpoint = TRUE;
296    }
297    if (GetDebugFlag (DEBUG_AGENT_FLAG_BREAK_ON_NEXT_SMI) == 1) {
298      if (mSkipBreakpoint) {
299        //
300        // Print warning message if ignore smm entry break
301        //
302        DebugPortWriteBuffer ((DEBUG_PORT_HANDLE) (UINTN)Mailbox->DebugPortHandle,
303                               (UINT8 *)mWarningMsgIgnoreSmmEntryBreak,
304                               AsciiStrLen (mWarningMsgIgnoreSmmEntryBreak)
305                               );
306      } else {
307        //
308        // If SMM entry break is set, SMM code will be break at here.
309        //
310        CpuBreakpoint ();
311      }
312    }
313    break;
314
315  case DEBUG_AGENT_INIT_EXIT_SMI:
316    Mailbox = GetMailboxPointer ();
317    //
318    // Clear Skip Breakpoint flag
319    //
320    mSkipBreakpoint = FALSE;
321    RestoreDebugRegister ();
322    break;
323
324  case DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64:
325    if (Context == NULL) {
326      DEBUG ((EFI_D_ERROR, "DebugAgent: Input parameter Context cannot be NULL!\n"));
327      CpuDeadLoop ();
328    } else {
329      Ia32Idtr =  (IA32_DESCRIPTOR *) Context;
330      Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base);
331      MailboxLocation = (UINT64 *) (UINTN) (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow +
332                                  (UINT32) (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16));
333      mMailboxPointer = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation);
334      VerifyMailboxChecksum (mMailboxPointer);
335      //
336      // Get original IDT address and size.
337      //
338      AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr);
339      IdtEntryCount = (UINT16) ((Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR));
340      if (IdtEntryCount < 33) {
341        Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33 - 1);
342        Idtr.Base  = (UINTN) &mIdtEntryTable;
343        ZeroMem (&mIdtEntryTable, Idtr.Limit + 1);
344        AsmWriteIdtr ((IA32_DESCRIPTOR *) &Idtr);
345      }
346
347      InitializeDebugIdt ();
348      //
349      // Initialize Debug Timer hardware and save its frequency
350      //
351      InitializeDebugTimer (&DebugTimerFrequency, TRUE);
352      UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency);
353      //
354      // Enable Debug Timer interrupt and CPU interrupt
355      //
356      SaveAndSetDebugTimerInterrupt (TRUE);
357      EnableInterrupts ();
358
359      FindAndReportModuleImageInfo (SIZE_4KB);
360    }
361    break;
362
363  default:
364    //
365    // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this
366    // Debug Agent library instance.
367    //
368    DEBUG ((EFI_D_ERROR, "Debug Agent: The InitFlag value is not allowed!\n"));
369    CpuDeadLoop ();
370    break;
371  }
372}
373
374