| /** @file | |
| Debug Agent library implementation. | |
| Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "SmmDebugAgentLib.h" | |
| DEBUG_AGENT_MAILBOX *mMailboxPointer = NULL; | |
| DEBUG_AGENT_MAILBOX mLocalMailbox; | |
| UINTN mSavedDebugRegisters[6]; | |
| IA32_IDT_GATE_DESCRIPTOR mIdtEntryTable[33]; | |
| BOOLEAN mSkipBreakpoint = FALSE; | |
| BOOLEAN mSmmDebugIdtInitFlag = FALSE; | |
| BOOLEAN mApicTimerRestore = FALSE; | |
| BOOLEAN mPeriodicMode; | |
| UINT32 mTimerCycle; | |
| UINTN mApicTimerDivisor; | |
| UINT8 mVector; | |
| CHAR8 mWarningMsgIgnoreSmmEntryBreak[] = "Ignore smmentrybreak setting for SMI issued during DXE debugging!\r\n"; | |
| /** | |
| Check if debug agent support multi-processor. | |
| @retval TRUE Multi-processor is supported. | |
| @retval FALSE Multi-processor is not supported. | |
| **/ | |
| BOOLEAN | |
| MultiProcessorDebugSupport ( | |
| VOID | |
| ) | |
| { | |
| return FALSE; | |
| } | |
| /** | |
| Read the Attach/Break-in symbols from the debug port. | |
| @param[in] Handle Pointer to Debug Port handle. | |
| @param[out] BreakSymbol Returned break symbol. | |
| @retval EFI_SUCCESS Read the symbol in BreakSymbol. | |
| @retval EFI_NOT_FOUND No read the break symbol. | |
| **/ | |
| EFI_STATUS | |
| DebugReadBreakSymbol ( | |
| IN DEBUG_PORT_HANDLE Handle, | |
| OUT UINT8 *BreakSymbol | |
| ) | |
| { | |
| // | |
| // Smm instance has no debug timer to poll break symbol. | |
| // | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| Get the pointer to Mailbox from the GUIDed HOB. | |
| @return Pointer to Mailbox. | |
| **/ | |
| DEBUG_AGENT_MAILBOX * | |
| GetMailboxFromHob ( | |
| VOID | |
| ) | |
| { | |
| EFI_HOB_GUID_TYPE *GuidHob; | |
| UINT64 *MailboxLocation; | |
| DEBUG_AGENT_MAILBOX *Mailbox; | |
| GuidHob = GetFirstGuidHob (&gEfiDebugAgentGuid); | |
| if (GuidHob == NULL) { | |
| return NULL; | |
| } | |
| MailboxLocation = (UINT64 *)(GET_GUID_HOB_DATA (GuidHob)); | |
| Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); | |
| VerifyMailboxChecksum (Mailbox); | |
| return Mailbox; | |
| } | |
| /** | |
| Get Debug Agent Mailbox pointer. | |
| @return Mailbox pointer. | |
| **/ | |
| DEBUG_AGENT_MAILBOX * | |
| GetMailboxPointer ( | |
| VOID | |
| ) | |
| { | |
| VerifyMailboxChecksum (mMailboxPointer); | |
| return mMailboxPointer; | |
| } | |
| /** | |
| Get debug port handle. | |
| @return Debug port handle. | |
| **/ | |
| DEBUG_PORT_HANDLE | |
| GetDebugPortHandle ( | |
| VOID | |
| ) | |
| { | |
| return (DEBUG_PORT_HANDLE)(UINTN)(GetMailboxPointer ()->DebugPortHandle); | |
| } | |
| /** | |
| Store debug register when SMI exit. | |
| **/ | |
| VOID | |
| SaveDebugRegister ( | |
| VOID | |
| ) | |
| { | |
| mSavedDebugRegisters[0] = AsmReadDr0 (); | |
| mSavedDebugRegisters[1] = AsmReadDr1 (); | |
| mSavedDebugRegisters[2] = AsmReadDr2 (); | |
| mSavedDebugRegisters[3] = AsmReadDr3 (); | |
| mSavedDebugRegisters[4] = AsmReadDr6 (); | |
| mSavedDebugRegisters[5] = AsmReadDr7 (); | |
| } | |
| /** | |
| Restore debug register when SMI exit. | |
| **/ | |
| VOID | |
| RestoreDebugRegister ( | |
| VOID | |
| ) | |
| { | |
| AsmWriteDr7 (0); | |
| AsmWriteDr0 (mSavedDebugRegisters[0]); | |
| AsmWriteDr1 (mSavedDebugRegisters[1]); | |
| AsmWriteDr2 (mSavedDebugRegisters[2]); | |
| AsmWriteDr3 (mSavedDebugRegisters[3]); | |
| AsmWriteDr6 (mSavedDebugRegisters[4]); | |
| AsmWriteDr7 (mSavedDebugRegisters[5]); | |
| } | |
| /** | |
| Initialize debug agent. | |
| This function is used to set up debug environment for source level debug | |
| in SMM code. | |
| If InitFlag is DEBUG_AGENT_INIT_SMM, it will override IDT table entries | |
| and initialize debug port. It will get debug agent Mailbox from GUIDed HOB, | |
| it it exists, debug agent wiil copied it into the local Mailbox in SMM space. | |
| it will override IDT table entries and initialize debug port. Context must | |
| point to a BOOLEAN if it's not NULL, which indicates SMM Debug Agent supported | |
| or not. | |
| If InitFlag is DEBUG_AGENT_INIT_ENTER_SMI, debug agent will save Debug | |
| Registers and get local Mailbox in SMM space. Context will be NULL. | |
| If InitFlag is DEBUG_AGENT_INIT_EXIT_SMI, debug agent will restore Debug | |
| Registers. Context will be NULL. | |
| @param[in] InitFlag Init flag is used to decide initialize process. | |
| @param[in] Context Context needed according to InitFlag. | |
| @param[in] Function Continue function called by debug agent library; it was | |
| optional. | |
| **/ | |
| VOID | |
| EFIAPI | |
| InitializeDebugAgent ( | |
| IN UINT32 InitFlag, | |
| IN VOID *Context OPTIONAL, | |
| IN DEBUG_AGENT_CONTINUE Function OPTIONAL | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT64 DebugPortHandle; | |
| IA32_IDT_GATE_DESCRIPTOR IdtEntry[33]; | |
| IA32_DESCRIPTOR IdtDescriptor; | |
| IA32_DESCRIPTOR *Ia32Idtr; | |
| IA32_IDT_ENTRY *Ia32IdtEntry; | |
| IA32_DESCRIPTOR Idtr; | |
| UINT16 IdtEntryCount; | |
| DEBUG_AGENT_MAILBOX *Mailbox; | |
| UINT64 *MailboxLocation; | |
| UINT32 DebugTimerFrequency; | |
| switch (InitFlag) { | |
| case DEBUG_AGENT_INIT_SMM: | |
| // | |
| // Install configuration table for persisted vector handoff info | |
| // | |
| Status = gSmst->SmmInstallConfigurationTable ( | |
| gSmst, | |
| &gEfiVectorHandoffTableGuid, | |
| (VOID *)&mVectorHandoffInfoDebugAgent[0], | |
| sizeof (EFI_VECTOR_HANDOFF_INFO) * mVectorHandoffInfoCount | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "DebugAgent: Cannot install configuration table for persisted vector handoff info!\n")); | |
| if (Context != NULL) { | |
| *(BOOLEAN *)Context = FALSE; | |
| } | |
| CpuDeadLoop (); | |
| } | |
| // | |
| // Check if Debug Agent initialized in DXE phase | |
| // | |
| Status = EfiGetSystemConfigurationTable (&gEfiDebugAgentGuid, (VOID **)&Mailbox); | |
| if ((Status == EFI_SUCCESS) && (Mailbox != NULL)) { | |
| VerifyMailboxChecksum (Mailbox); | |
| mMailboxPointer = Mailbox; | |
| if (Context != NULL) { | |
| *(BOOLEAN *)Context = TRUE; | |
| } | |
| break; | |
| } | |
| // | |
| // Check if Debug Agent initialized in SEC/PEI phase | |
| // | |
| Mailbox = GetMailboxFromHob (); | |
| if (Mailbox != NULL) { | |
| mMailboxPointer = Mailbox; | |
| if (Context != NULL) { | |
| *(BOOLEAN *)Context = TRUE; | |
| } | |
| break; | |
| } | |
| // | |
| // Debug Agent was not initialized before, use the local mailbox. | |
| // | |
| ZeroMem (&mLocalMailbox, sizeof (DEBUG_AGENT_MAILBOX)); | |
| Mailbox = &mLocalMailbox; | |
| // | |
| // Save original IDT entries | |
| // | |
| AsmReadIdtr (&IdtDescriptor); | |
| CopyMem (&IdtEntry, (VOID *)IdtDescriptor.Base, 33 * sizeof (IA32_IDT_GATE_DESCRIPTOR)); | |
| // | |
| // Initialized Debug Agent | |
| // | |
| InitializeDebugIdt (); | |
| // | |
| // Initialize Debug Timer hardware and save its frequency | |
| // | |
| InitializeDebugTimer (&DebugTimerFrequency, TRUE); | |
| UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); | |
| DebugPortHandle = (UINT64)(UINTN)DebugPortInitialize ((DEBUG_PORT_HANDLE)(UINTN)Mailbox->DebugPortHandle, NULL); | |
| UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle); | |
| mMailboxPointer = Mailbox; | |
| // | |
| // Trigger one software interrupt to inform HOST | |
| // | |
| TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE); | |
| SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1); | |
| // | |
| // Memory has been ready | |
| // | |
| if (IsHostAttached ()) { | |
| // | |
| // Trigger one software interrupt to inform HOST | |
| // | |
| TriggerSoftInterrupt (MEMORY_READY_SIGNATURE); | |
| } | |
| // | |
| // Find and report PE/COFF image info to HOST | |
| // | |
| FindAndReportModuleImageInfo (SIZE_4KB); | |
| // | |
| // Restore saved IDT entries | |
| // | |
| CopyMem ((VOID *)IdtDescriptor.Base, &IdtEntry, 33 * sizeof (IA32_IDT_GATE_DESCRIPTOR)); | |
| if (Context != NULL) { | |
| *(BOOLEAN *)Context = TRUE; | |
| } | |
| break; | |
| case DEBUG_AGENT_INIT_ENTER_SMI: | |
| SaveDebugRegister (); | |
| if (!mSmmDebugIdtInitFlag) { | |
| // | |
| // We only need to initialize Debug IDT table at first SMI entry | |
| // after SMM relocation. | |
| // | |
| InitializeDebugIdt (); | |
| mSmmDebugIdtInitFlag = TRUE; | |
| } | |
| // | |
| // Check if CPU APIC Timer is working, otherwise initialize it. | |
| // | |
| InitializeLocalApicSoftwareEnable (TRUE); | |
| GetApicTimerState (&mApicTimerDivisor, &mPeriodicMode, &mVector); | |
| mTimerCycle = GetApicTimerInitCount (); | |
| if (!mPeriodicMode || (mTimerCycle == 0)) { | |
| mApicTimerRestore = TRUE; | |
| InitializeDebugTimer (NULL, FALSE); | |
| } | |
| Mailbox = GetMailboxPointer (); | |
| if (GetDebugFlag (DEBUG_AGENT_FLAG_AGENT_IN_PROGRESS) == 1) { | |
| // | |
| // If Debug Agent has been communication state with HOST, we need skip | |
| // any break points set in SMM, set Skip Breakpoint flag | |
| // | |
| mSkipBreakpoint = TRUE; | |
| } | |
| if (GetDebugFlag (DEBUG_AGENT_FLAG_BREAK_ON_NEXT_SMI) == 1) { | |
| if (mSkipBreakpoint) { | |
| // | |
| // Print warning message if ignore smm entry break | |
| // | |
| DebugPortWriteBuffer ( | |
| (DEBUG_PORT_HANDLE)(UINTN)Mailbox->DebugPortHandle, | |
| (UINT8 *)mWarningMsgIgnoreSmmEntryBreak, | |
| AsciiStrLen (mWarningMsgIgnoreSmmEntryBreak) | |
| ); | |
| } else { | |
| // | |
| // If SMM entry break is set, SMM code will be break at here. | |
| // | |
| CpuBreakpoint (); | |
| } | |
| } | |
| break; | |
| case DEBUG_AGENT_INIT_EXIT_SMI: | |
| Mailbox = GetMailboxPointer (); | |
| // | |
| // Clear Skip Breakpoint flag | |
| // | |
| mSkipBreakpoint = FALSE; | |
| RestoreDebugRegister (); | |
| // | |
| // Restore APIC Timer | |
| // | |
| if (mApicTimerRestore) { | |
| InitializeApicTimer (mApicTimerDivisor, mTimerCycle, mPeriodicMode, mVector); | |
| mApicTimerRestore = FALSE; | |
| } | |
| break; | |
| case DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64: | |
| if (Context == NULL) { | |
| DEBUG ((DEBUG_ERROR, "DebugAgent: Input parameter Context cannot be NULL!\n")); | |
| CpuDeadLoop (); | |
| } else { | |
| Ia32Idtr = (IA32_DESCRIPTOR *)Context; | |
| Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base); | |
| MailboxLocation = (UINT64 *)((UINTN)Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow + | |
| ((UINTN)Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16)); | |
| mMailboxPointer = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation); | |
| VerifyMailboxChecksum (mMailboxPointer); | |
| // | |
| // Get original IDT address and size. | |
| // | |
| AsmReadIdtr ((IA32_DESCRIPTOR *)&Idtr); | |
| IdtEntryCount = (UINT16)((Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)); | |
| if (IdtEntryCount < 33) { | |
| Idtr.Limit = (UINT16)(sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33 - 1); | |
| Idtr.Base = (UINTN)&mIdtEntryTable; | |
| ZeroMem (&mIdtEntryTable, Idtr.Limit + 1); | |
| AsmWriteIdtr ((IA32_DESCRIPTOR *)&Idtr); | |
| } | |
| InitializeDebugIdt (); | |
| // | |
| // Initialize Debug Timer hardware and save its frequency | |
| // | |
| InitializeDebugTimer (&DebugTimerFrequency, TRUE); | |
| UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency); | |
| // | |
| // Enable Debug Timer interrupt and CPU interrupt | |
| // | |
| SaveAndSetDebugTimerInterrupt (TRUE); | |
| EnableInterrupts (); | |
| FindAndReportModuleImageInfo (SIZE_4KB); | |
| } | |
| break; | |
| default: | |
| // | |
| // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this | |
| // Debug Agent library instance. | |
| // | |
| DEBUG ((DEBUG_ERROR, "Debug Agent: The InitFlag value is not allowed!\n")); | |
| CpuDeadLoop (); | |
| break; | |
| } | |
| } |