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