| /** @file | |
| Commond Debug Agent library implementition. It mainly includes | |
| the first C function called by exception/interrupt handlers, | |
| read/write debug packet to communication with HOST based on transfer | |
| protocol. | |
| Copyright (c) 2010, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php. | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "DebugAgent.h" | |
| #include "Ia32/DebugException.h" | |
| /** | |
| Check if HOST is connected based on Mailbox. | |
| @retval TRUE HOST is connected. | |
| @retval FALSE HOST is not connected. | |
| **/ | |
| BOOLEAN | |
| IsHostConnected ( | |
| VOID | |
| ) | |
| { | |
| DEBUG_AGENT_MAILBOX *Mailbox; | |
| Mailbox = GetMailboxPointer (); | |
| if (Mailbox->DebugFlag.Bits.HostPresent == 1) { | |
| return TRUE; | |
| } else { | |
| return FALSE; | |
| } | |
| } | |
| /** | |
| Set HOST connect flag in Mailbox. | |
| **/ | |
| VOID | |
| SetHostConnectedFlag ( | |
| VOID | |
| ) | |
| { | |
| DEBUG_AGENT_MAILBOX *Mailbox; | |
| Mailbox = GetMailboxPointer (); | |
| Mailbox->DebugFlag.Bits.HostPresent = 1; | |
| } | |
| /** | |
| Set debug flag of Debug Agent in Mailbox. | |
| @param DebugFlag Debug Flag defined by transfer protocol. | |
| **/ | |
| VOID | |
| SetDebugFlag ( | |
| IN UINT32 DebugFlag | |
| ) | |
| { | |
| DEBUG_AGENT_MAILBOX *Mailbox; | |
| Mailbox = GetMailboxPointer (); | |
| if ((DebugFlag & SOFT_DEBUGGER_SETTING_SMM_ENTRY_BREAK) != 0) { | |
| Mailbox->DebugFlag.Bits.BreakOnNextSmi = 1; | |
| } else { | |
| Mailbox->DebugFlag.Bits.BreakOnNextSmi = 0; | |
| } | |
| } | |
| /** | |
| Exectue GO command. | |
| @param[in] CpuContext Pointer to saved CPU context. | |
| **/ | |
| VOID | |
| CommandGo ( | |
| IN DEBUG_CPU_CONTEXT *CpuContext | |
| ) | |
| { | |
| IA32_EFLAGS32 *Eflags; | |
| Eflags = (IA32_EFLAGS32 *) &CpuContext->Eflags; | |
| Eflags->Bits.TF = 0; | |
| Eflags->Bits.RF = 1; | |
| } | |
| /** | |
| Exectue Stepping command. | |
| @param[in] CpuContext Pointer to saved CPU context. | |
| **/ | |
| VOID | |
| CommandStepping ( | |
| IN DEBUG_CPU_CONTEXT *CpuContext | |
| ) | |
| { | |
| IA32_EFLAGS32 *Eflags; | |
| Eflags = (IA32_EFLAGS32 *) &CpuContext->Eflags; | |
| Eflags->Bits.TF = 1; | |
| Eflags->Bits.RF = 1; | |
| } | |
| /** | |
| Set debug register for hardware breakpoint. | |
| @param[in] CpuContext Pointer to saved CPU context. | |
| @param[in] SetHwBreakpoint Hardware breakpoint to be set. | |
| **/ | |
| VOID | |
| SetDebugRegister ( | |
| IN DEBUG_CPU_CONTEXT *CpuContext, | |
| IN DEBUG_DATA_SET_HW_BREAKPOINT *SetHwBreakpoint | |
| ) | |
| { | |
| UINT8 RegisterIndex; | |
| UINTN Dr7Value; | |
| RegisterIndex = SetHwBreakpoint->Type.Index; | |
| // | |
| // Set debug address | |
| // | |
| * ((UINTN *) &CpuContext->Dr0 + RegisterIndex) = (UINTN) SetHwBreakpoint->Address; | |
| Dr7Value = CpuContext->Dr7; | |
| // | |
| // Enable Gx, Lx | |
| // | |
| Dr7Value |= 0x3 << (RegisterIndex * 2); | |
| // | |
| // Set RWx and Lenx | |
| // | |
| Dr7Value &= ~(0xf0000 << (RegisterIndex * 4)); | |
| Dr7Value |= (SetHwBreakpoint->Type.Length | SetHwBreakpoint->Type.Access) << (RegisterIndex * 4); | |
| // | |
| // Enable GE, LE | |
| // | |
| Dr7Value |= 0x300; | |
| CpuContext->Dr7 = Dr7Value; | |
| } | |
| /** | |
| Clear debug register for hardware breakpoint. | |
| @param[in] CpuContext Pointer to saved CPU context. | |
| @param[in] ClearHwBreakpoint Hardware breakpoint to be cleared. | |
| **/ | |
| VOID | |
| ClearDebugRegister ( | |
| IN DEBUG_CPU_CONTEXT *CpuContext, | |
| IN DEBUG_DATA_CLEAR_HW_BREAKPOINT *ClearHwBreakpoint | |
| ) | |
| { | |
| if ((ClearHwBreakpoint->IndexMask & BIT0) != 0) { | |
| CpuContext->Dr0 = 0; | |
| CpuContext->Dr7 &= ~(0x3 << 0); | |
| } | |
| if ((ClearHwBreakpoint->IndexMask & BIT1) != 0) { | |
| CpuContext->Dr1 = 0; | |
| CpuContext->Dr7 &= ~(0x3 << 2); | |
| } | |
| if ((ClearHwBreakpoint->IndexMask & BIT2) != 0) { | |
| CpuContext->Dr2 = 0; | |
| CpuContext->Dr7 &= ~(0x3 << 4); | |
| } | |
| if ((ClearHwBreakpoint->IndexMask & BIT3) != 0) { | |
| CpuContext->Dr3 = 0; | |
| CpuContext->Dr7 &= ~(0x3 << 6); | |
| } | |
| } | |
| /** | |
| Send acknowledge packet to HOST. | |
| @param[in] AckCommand Type of Acknowledge packet. | |
| **/ | |
| VOID | |
| SendAckPacket ( | |
| IN UINT8 AckCommand | |
| ) | |
| { | |
| DEBUG_COMMAND_HEADER DebugCommonHeader; | |
| DEBUG_PORT_HANDLE Handle; | |
| Handle = GetDebugPortHandle(); | |
| DebugCommonHeader.StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL; | |
| DebugCommonHeader.Command = AckCommand; | |
| DebugCommonHeader.DataLength = 0; | |
| DebugPortWriteBuffer (Handle, (UINT8 *) &DebugCommonHeader, sizeof (DEBUG_COMMAND_HEADER)); | |
| } | |
| /** | |
| Receive acknowledge packet from HOST in specified time. | |
| @param[out] Ack Returned acknowlege type from HOST. | |
| @param[in] Timeout Time out value to wait for acknowlege from HOST. | |
| The unit is microsecond. | |
| @param[out] BreakReceived If BreakReceived is not NULL, | |
| TRUE is retured if break-in symbol received. | |
| FALSE is retured if break-in symbol not received. | |
| @retval RETRUEN_SUCCESS Succeed to receive acknowlege packet from HOST, | |
| the type of acknowlege packet saved in Ack. | |
| @retval RETURN_TIMEOUT Specified timeout value was up. | |
| **/ | |
| RETURN_STATUS | |
| ReceiveAckPacket ( | |
| OUT UINT8 *Ack, | |
| IN UINTN Timeout, | |
| OUT BOOLEAN *BreakReceived OPTIONAL | |
| ) | |
| { | |
| DEBUG_COMMAND_HEADER DebugCommonHeader; | |
| DEBUG_PORT_HANDLE Handle; | |
| Handle = GetDebugPortHandle(); | |
| while (TRUE) { | |
| if (DebugPortReadBuffer (Handle, (UINT8 *) &DebugCommonHeader.StartSymbol, 1, Timeout) == 0) { | |
| return RETURN_TIMEOUT; | |
| } | |
| if (DebugCommonHeader.StartSymbol == DEBUG_STARTING_SYMBOL_BREAK) { | |
| if (BreakReceived != NULL) { | |
| SendAckPacket (DEBUG_COMMAND_HALT_DEFERRED); | |
| *BreakReceived = TRUE; | |
| } | |
| } | |
| if (DebugCommonHeader.StartSymbol == DEBUG_STARTING_SYMBOL_NORMAL) { | |
| break; | |
| } | |
| } | |
| if (DebugPortReadBuffer (Handle, (UINT8 *)&DebugCommonHeader.Command, sizeof (DEBUG_COMMAND_HEADER) - 1, Timeout) == 0) { | |
| return RETURN_TIMEOUT; | |
| } | |
| *Ack = DebugCommonHeader.Command; | |
| return RETURN_SUCCESS; | |
| } | |
| /** | |
| Receive acknowledge packet OK from HOST in specified time. | |
| @param[in] Timeout Time out value to wait for acknowlege from HOST. | |
| The unit is microsecond. | |
| @param[out] BreakReceived If BreakReceived is not NULL, | |
| TRUE is retured if break-in symbol received. | |
| FALSE is retured if break-in symbol not received. | |
| @retval RETRUEN_SUCCESS Succeed to receive acknowlege packet from HOST, | |
| the type of acknowlege packet saved in Ack. | |
| @retval RETURN_TIMEOUT Specified timeout value was up. | |
| **/ | |
| RETURN_STATUS | |
| WaitForAckPacketOK ( | |
| IN UINTN Timeout, | |
| OUT BOOLEAN *BreakReceived OPTIONAL | |
| ) | |
| { | |
| RETURN_STATUS Status; | |
| UINT8 Ack; | |
| while (TRUE) { | |
| Status = ReceiveAckPacket (&Ack, Timeout, BreakReceived); | |
| if ((Status == RETURN_SUCCESS && Ack == DEBUG_COMMAND_OK) || | |
| Status == RETURN_TIMEOUT) { | |
| break; | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| Receive valid packet from HOST. | |
| @param[out] InputPacket Buffer to receive packet. | |
| @param[out] BreakReceived TRUE means break-in symbol received. | |
| FALSE means break-in symbol not received. | |
| @retval RETURN_SUCCESS A valid package was reveived in InputPacket. | |
| @retval RETURN_NOT_READY No valid start symbol received. | |
| @retval RETURN_TIMEOUT Timeout occurs. | |
| **/ | |
| RETURN_STATUS | |
| ReceivePacket ( | |
| OUT UINT8 *InputPacket, | |
| OUT BOOLEAN *BreakReceived | |
| ) | |
| { | |
| DEBUG_COMMAND_HEADER *DebugHeader; | |
| UINTN Received; | |
| DEBUG_PORT_HANDLE Handle; | |
| Handle = GetDebugPortHandle(); | |
| // | |
| // Find the valid start symbol | |
| // | |
| DebugPortReadBuffer (Handle, InputPacket, 1, 0); | |
| if (*InputPacket == DEBUG_STARTING_SYMBOL_BREAK) { | |
| *BreakReceived = TRUE; | |
| SendAckPacket (DEBUG_COMMAND_HALT_DEFERRED); | |
| } | |
| if (*InputPacket != DEBUG_STARTING_SYMBOL_NORMAL) { | |
| return RETURN_NOT_READY; | |
| } | |
| // | |
| // Read Package header | |
| // | |
| Received = DebugPortReadBuffer (Handle, InputPacket + 1, sizeof(DEBUG_COMMAND_HEADER_NO_START_SYMBOL), 0); | |
| if (Received == 0) { | |
| return RETURN_TIMEOUT; | |
| } | |
| DebugHeader = (DEBUG_COMMAND_HEADER *) InputPacket; | |
| // | |
| // Read the payload if has | |
| // | |
| if (DebugHeader->DataLength > 0 && DebugHeader->DataLength < (DEBUG_DATA_MAXIMUM_REAL_DATA - sizeof(DEBUG_COMMAND_HEADER))) { | |
| InputPacket = InputPacket + 1 + Received; | |
| Received = DebugPortReadBuffer (Handle, InputPacket, DebugHeader->DataLength, 0); | |
| if (Received == 0) { | |
| return RETURN_TIMEOUT; | |
| } | |
| } | |
| return RETURN_SUCCESS; | |
| } | |
| /** | |
| Get current break cause. | |
| @param[in] Vector Vector value of exception or interrupt. | |
| @param[in] CpuContext Pointer to save CPU context. | |
| @return The type of break cause defined by XXXX | |
| **/ | |
| UINT8 | |
| GetBreakCause ( | |
| IN UINTN Vector, | |
| IN DEBUG_CPU_CONTEXT *CpuContext | |
| ) | |
| { | |
| UINT8 Cause; | |
| Cause = DEBUG_DATA_BREAK_CAUSE_UNKNOWN; | |
| switch (Vector) { | |
| case DEBUG_INT1_VECTOR: | |
| case DEBUG_INT3_VECTOR: | |
| if (Vector == DEBUG_INT1_VECTOR) { | |
| // | |
| // INT 1 | |
| // | |
| if ((CpuContext->Dr6 & BIT14) != 0) { | |
| Cause = DEBUG_DATA_BREAK_CAUSE_STEPPING; | |
| // | |
| // If it's single step, no need to check DR0, to ensure single step work in PeCoffExtraActionLib | |
| // (right after triggering a breakpoint to report image load/unload). | |
| // | |
| return Cause; | |
| } else { | |
| Cause = DEBUG_DATA_BREAK_CAUSE_HW_BREAKPOINT; | |
| } | |
| } else { | |
| // | |
| // INT 3 | |
| // | |
| Cause = DEBUG_DATA_BREAK_CAUSE_SW_BREAKPOINT; | |
| } | |
| switch (CpuContext->Dr0) { | |
| case IMAGE_LOAD_SIGNATURE: | |
| case IMAGE_UNLOAD_SIGNATURE: | |
| if (CpuContext->Dr3 == IO_PORT_BREAKPOINT_ADDRESS) { | |
| Cause = (UINT8) ((CpuContext->Dr0 == IMAGE_LOAD_SIGNATURE) ? | |
| DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD : DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD); | |
| } | |
| break; | |
| case SOFT_INTERRUPT_SIGNATURE: | |
| if (CpuContext->Dr1 == MEMORY_READY_SIGNATURE) { | |
| Cause = DEBUG_DATA_BREAK_CAUSE_MEMORY_READY; | |
| CpuContext->Dr0 = 0; | |
| } else if (CpuContext->Dr1 == SYSTEM_RESET_SIGNATURE) { | |
| Cause = DEBUG_DATA_BREAK_CAUSE_SYSTEM_RESET; | |
| CpuContext->Dr0 = 0; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case DEBUG_TIMER_VECTOR: | |
| Cause = DEBUG_DATA_BREAK_CAUSE_USER_HALT; | |
| break; | |
| default: | |
| if (Vector < 20) { | |
| Cause = DEBUG_DATA_BREAK_CAUSE_EXCEPTION; | |
| } | |
| break; | |
| } | |
| return Cause; | |
| } | |
| /** | |
| Send packet with response data to HOST. | |
| @param[in] CpuContext Pointer to saved CPU context. | |
| @param[in] Data Pointer to response data buffer. | |
| @param[in] DataSize Size of response data in byte. | |
| @retval RETURN_SUCCESS Response data was sent successfully. | |
| @retval RETURN_DEVICE_ERROR Cannot receive DEBUG_COMMAND_OK from HOST. | |
| **/ | |
| RETURN_STATUS | |
| SendDataResponsePacket ( | |
| IN DEBUG_CPU_CONTEXT *CpuContext, | |
| IN UINT8 *Data, | |
| IN UINT16 DataSize | |
| ) | |
| { | |
| UINT8 PacketHeader[DEBUG_DATA_MAXIMUM_LENGTH_FOR_SMALL_COMMANDS]; | |
| BOOLEAN LastPacket; | |
| UINT8 Ack; | |
| UINT8 PacketData[DEBUG_DATA_MAXIMUM_REAL_DATA]; | |
| DEBUG_PORT_HANDLE Handle; | |
| Handle = GetDebugPortHandle(); | |
| ((DEBUG_COMMAND_HEADER *)PacketHeader)->StartSymbol = DEBUG_STARTING_SYMBOL_NORMAL; | |
| while (TRUE) { | |
| if (DataSize <= DEBUG_DATA_MAXIMUM_REAL_DATA) { | |
| LastPacket = TRUE; | |
| ((DEBUG_COMMAND_HEADER *)PacketHeader)->Command = DEBUG_COMMAND_OK; | |
| ((DEBUG_COMMAND_HEADER *)PacketHeader)->DataLength = (UINT8) DataSize; | |
| CopyMem (PacketData, Data, DataSize); | |
| } else { | |
| LastPacket = FALSE; | |
| ((DEBUG_COMMAND_HEADER *)PacketHeader)->Command = DEBUG_COMMAND_IN_PROGRESS; | |
| ((DEBUG_COMMAND_HEADER *)PacketHeader)->DataLength = DEBUG_DATA_MAXIMUM_REAL_DATA; | |
| CopyMem (PacketData, Data, DEBUG_DATA_MAXIMUM_REAL_DATA); | |
| } | |
| DebugPortWriteBuffer (Handle, PacketHeader, sizeof (DEBUG_COMMAND_HEADER)); | |
| DebugPortWriteBuffer (Handle, PacketData, ((DEBUG_COMMAND_HEADER *)PacketHeader)->DataLength); | |
| ReceiveAckPacket(&Ack, 0, NULL); | |
| switch (Ack) { | |
| case DEBUG_COMMAND_RESEND: | |
| // | |
| // Send the packet again | |
| // | |
| break; | |
| case DEBUG_COMMAND_CONTINUE: | |
| // | |
| // Send the rest packet | |
| // | |
| Data += DEBUG_DATA_MAXIMUM_REAL_DATA; | |
| DataSize -= DEBUG_DATA_MAXIMUM_REAL_DATA; | |
| break; | |
| case DEBUG_COMMAND_OK: | |
| if (LastPacket) { | |
| // | |
| // If this is the last packet, return RETURN_SUCCESS. | |
| // | |
| return RETURN_SUCCESS; | |
| } else { | |
| return RETURN_DEVICE_ERROR; | |
| } | |
| default: | |
| return RETURN_DEVICE_ERROR; | |
| } | |
| } | |
| } | |
| /** | |
| Send break cause packet to HOST. | |
| @param[in] Vector Vector value of exception or interrutp. | |
| @param[in] CpuContext Pointer to save CPU context. | |
| @retval RETURN_SUCCESS Response data was sent successfully. | |
| @retval RETURN_DEVICE_ERROR Cannot receive DEBUG_COMMAND_OK from HOST. | |
| **/ | |
| RETURN_STATUS | |
| SendBreakCausePacket ( | |
| IN UINTN Vector, | |
| IN DEBUG_CPU_CONTEXT *CpuContext | |
| ) | |
| { | |
| DEBUG_DATA_RESPONSE_BREAK_CAUSE DebugDataBreakCause; | |
| DebugDataBreakCause.StopAddress = CpuContext->Eip; | |
| DebugDataBreakCause.Cause = GetBreakCause (Vector, CpuContext); | |
| return SendDataResponsePacket (CpuContext, (UINT8 *) &DebugDataBreakCause, (UINT16) sizeof (DEBUG_DATA_RESPONSE_BREAK_CAUSE)); | |
| } | |
| /** | |
| The main function to process communication with HOST. | |
| It received the command packet from HOST, and sent response data packet to HOST. | |
| @param[in] Vector Vector value of exception or interrutp. | |
| @param[in, out] CpuContext Pointer to saved CPU context. | |
| @param[in] BreakReceived TRUE means break-in symbol received. | |
| FALSE means break-in symbol not received. | |
| **/ | |
| VOID | |
| CommandCommunication ( | |
| IN UINTN Vector, | |
| IN OUT DEBUG_CPU_CONTEXT *CpuContext, | |
| IN BOOLEAN BreakReceived | |
| ) | |
| { | |
| RETURN_STATUS Status; | |
| UINT8 InputPacketBuffer[DEBUG_DATA_MAXIMUM_LENGTH_FOR_SMALL_COMMANDS]; | |
| DEBUG_COMMAND_HEADER *DebugHeader; | |
| UINT8 Data8; | |
| UINT32 Data32; | |
| UINT64 Data64; | |
| UINTN DataN; | |
| DEBUG_DATA_READ_MEMORY_8 *MemoryRead; | |
| DEBUG_DATA_WRITE_MEMORY_8 *MemoryWrite; | |
| DEBUG_DATA_READ_IO *IoRead; | |
| DEBUG_DATA_WRITE_IO *IoWrite; | |
| DEBUG_DATA_READ_REGISTER *RegisterRead; | |
| DEBUG_DATA_WRITE_REGISTER *RegisterWrite; | |
| UINT8 *RegisterBuffer; | |
| DEBUG_DATA_READ_MSR *MsrRegisterRead; | |
| DEBUG_DATA_WRITE_MSR *MsrRegisterWrite; | |
| DEBUG_DATA_REPONSE_READ_REGISTER_GROUP_SEGLIM RegisterGroupSegLim; | |
| DEBUG_DATA_REPONSE_READ_REGISTER_GROUP_SEGBASE RegisterGroupSegBase; | |
| DEBUG_DATA_RESPONSE_GET_REVISION DebugAgentRevision; | |
| BOOLEAN HaltDeferred; | |
| DEBUG_DATA_RESPONSE_GET_EXCEPTION Exception; | |
| UINT32 ProcessorIndex; | |
| DEBUG_PORT_HANDLE Handle; | |
| Handle = GetDebugPortHandle(); | |
| ProcessorIndex = 0; | |
| HaltDeferred = BreakReceived; | |
| if (MultiProcessorDebugSupport) { | |
| ProcessorIndex = GetProcessorIndex (); | |
| SetCpuStopFlagByIndex (ProcessorIndex, TRUE); | |
| } | |
| while (TRUE) { | |
| if (MultiProcessorDebugSupport) { | |
| if (mDebugMpContext.ViewPointIndex != ProcessorIndex) { | |
| if (mDebugMpContext.RunCommandSet) { | |
| SetCpuStopFlagByIndex (ProcessorIndex, FALSE); | |
| CommandGo (CpuContext); | |
| break; | |
| } else { | |
| continue; | |
| } | |
| } | |
| } | |
| AcquireDebugPortControl (); | |
| Status = ReceivePacket (InputPacketBuffer, &BreakReceived); | |
| if (BreakReceived) { | |
| HaltDeferred = TRUE; | |
| BreakReceived = FALSE; | |
| } | |
| if (Status != RETURN_SUCCESS) { | |
| ReleaseDebugPortControl (); | |
| continue; | |
| } | |
| Data8 = 1; | |
| DebugHeader =(DEBUG_COMMAND_HEADER *) InputPacketBuffer; | |
| switch (DebugHeader->Command) { | |
| case DEBUG_COMMAND_RESET: | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| ReleaseDebugPortControl (); | |
| ResetCold (); | |
| // | |
| // Wait for reset | |
| // | |
| CpuDeadLoop (); | |
| break; | |
| case DEBUG_COMMAND_GO: | |
| CommandGo (CpuContext); | |
| if (!HaltDeferred) { | |
| // | |
| // If no HALT command received when being in-active mode | |
| // | |
| if (MultiProcessorDebugSupport) { | |
| Data32 = FindCpuNotRunning (); | |
| if (Data32 != -1) { | |
| // | |
| // If there are still others processors being in break state, | |
| // send OK packet to HOST to finish this go command | |
| // | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| CpuPause (); | |
| // | |
| // Set current view to the next breaking processor | |
| // | |
| mDebugMpContext.ViewPointIndex = Data32; | |
| mDebugMpContext.BreakAtCpuIndex = mDebugMpContext.ViewPointIndex; | |
| SetCpuBreakFlagByIndex (mDebugMpContext.ViewPointIndex, FALSE); | |
| // | |
| // Send break packet to HOST and exit to wait for command packet from HOST. | |
| // | |
| SendAckPacket (DEBUG_COMMAND_BREAK_POINT); | |
| WaitForAckPacketOK (0, &BreakReceived); | |
| ReleaseDebugPortControl (); | |
| break; | |
| } | |
| // | |
| // If no else processor break, set stop bitmask, | |
| // and set Running flag for all processors. | |
| // | |
| SetCpuStopFlagByIndex (ProcessorIndex, FALSE); | |
| SetCpuRunningFlag (TRUE); | |
| CpuPause (); | |
| // | |
| // Wait for all processors are in running state | |
| // | |
| while (TRUE) { | |
| if (IsAllCpuRunning ()) { | |
| break; | |
| } | |
| } | |
| // | |
| // Set BSP to be current view point. | |
| // | |
| SetDebugViewPoint (mDebugMpContext.BspIndex); | |
| CpuPause (); | |
| // | |
| // Clear breaking processor index and running flag | |
| // | |
| mDebugMpContext.BreakAtCpuIndex = (UINT32) (-1); | |
| SetCpuRunningFlag (FALSE); | |
| } | |
| // | |
| // Send OK packet to HOST to finish this go command | |
| // | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| ReleaseDebugPortControl (); | |
| return; | |
| } else { | |
| // | |
| // If reveived HALT command, need to defer the GO command | |
| // | |
| SendAckPacket (DEBUG_COMMAND_HALT_PROCESSED); | |
| HaltDeferred = FALSE; | |
| Data8 = GetBreakCause (Vector, CpuContext); | |
| if (Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD || Data8 == DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD) { | |
| CpuContext->Dr0 = 0; | |
| CpuContext->Dr3 = 0; | |
| } | |
| Vector = DEBUG_TIMER_VECTOR; | |
| } | |
| break; | |
| case DEBUG_COMMAND_BREAK_CAUSE: | |
| if (MultiProcessorDebugSupport && ProcessorIndex != mDebugMpContext.BreakAtCpuIndex) { | |
| Status = SendBreakCausePacket (DEBUG_TIMER_VECTOR, CpuContext); | |
| } else { | |
| Status = SendBreakCausePacket (Vector, CpuContext); | |
| } | |
| break; | |
| case DEBUG_COMMAND_SET_HW_BREAKPOINT: | |
| SetDebugRegister (CpuContext, (DEBUG_DATA_SET_HW_BREAKPOINT *) (DebugHeader + 1)); | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| break; | |
| case DEBUG_COMMAND_CLEAR_HW_BREAKPOINT: | |
| ClearDebugRegister (CpuContext, (DEBUG_DATA_CLEAR_HW_BREAKPOINT *) (DebugHeader + 1)); | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| break; | |
| case DEBUG_COMMAND_SINGLE_STEPPING: | |
| CommandStepping (CpuContext); | |
| mDebugMpContext.BreakAtCpuIndex = (UINT32) (-1); | |
| ReleaseDebugPortControl (); | |
| // | |
| // Executing stepping command directly without sending ACK packet. | |
| // | |
| return; | |
| case DEBUG_COMMAND_SET_SW_BREAKPOINT: | |
| Data64 = (UINTN) (((DEBUG_DATA_SET_SW_BREAKPOINT *) (DebugHeader + 1))->Address); | |
| Data8 = *(UINT8 *) (UINTN) Data64; | |
| *(UINT8 *) (UINTN) Data64 = DEBUG_SW_BREAKPOINT_SYMBOL; | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) &Data8, (UINT16) sizeof (UINT8)); | |
| break; | |
| case DEBUG_COMMAND_READ_MEMORY_64: | |
| Data8 *= 2; | |
| case DEBUG_COMMAND_READ_MEMORY_32: | |
| Data8 *= 2; | |
| case DEBUG_COMMAND_READ_MEMORY_16: | |
| Data8 *= 2; | |
| case DEBUG_COMMAND_READ_MEMORY_8: | |
| MemoryRead = (DEBUG_DATA_READ_MEMORY_8 *) (DebugHeader + 1); | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) (UINTN) MemoryRead->Address, (UINT16) (MemoryRead->Count * Data8)); | |
| break; | |
| case DEBUG_COMMAND_WRITE_MEMORY_64: | |
| Data8 *= 2; | |
| case DEBUG_COMMAND_WRITE_MEMORY_32: | |
| Data8 *= 2; | |
| case DEBUG_COMMAND_WRITE_MEMORY_16: | |
| Data8 *= 2; | |
| case DEBUG_COMMAND_WRITE_MEMORY_8: | |
| MemoryWrite = (DEBUG_DATA_WRITE_MEMORY_8 *) (DebugHeader + 1); | |
| CopyMem ((VOID *) (UINTN) MemoryWrite->Address, &MemoryWrite->Data, MemoryWrite->Count * Data8); | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| break; | |
| case DEBUG_COMMAND_READ_IO: | |
| IoRead = (DEBUG_DATA_READ_IO *) (DebugHeader + 1); | |
| switch (IoRead->Width) { | |
| case 1: | |
| Data64 = IoRead8 (IoRead->Port); | |
| break; | |
| case 2: | |
| Data64 = IoRead16 (IoRead->Port); | |
| break; | |
| case 4: | |
| Data64 = IoRead32 (IoRead->Port); | |
| break; | |
| case 8: | |
| Data64 = IoRead64 (IoRead->Port); | |
| break; | |
| default: | |
| Data64 = (UINT64) -1; | |
| } | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) &Data64, IoRead->Width); | |
| break; | |
| case DEBUG_COMMAND_WRITE_IO: | |
| IoWrite = (DEBUG_DATA_WRITE_IO *) (DebugHeader + 1); | |
| switch (IoWrite->Width) { | |
| case 1: | |
| Data64 = IoWrite8 (IoWrite->Port, *(UINT8 *) &IoWrite->Data); | |
| break; | |
| case 2: | |
| Data64 = IoWrite16 (IoWrite->Port, *(UINT16 *) &IoWrite->Data); | |
| break; | |
| case 4: | |
| Data64 = IoWrite32 (IoWrite->Port, *(UINT32 *) &IoWrite->Data); | |
| break; | |
| case 8: | |
| Data64 = IoWrite64 (IoWrite->Port, *(UINT64 *) &IoWrite->Data); | |
| break; | |
| default: | |
| Data64 = (UINT64) -1; | |
| } | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| break; | |
| case DEBUG_COMMAND_READ_REGISTER: | |
| RegisterRead = (DEBUG_DATA_READ_REGISTER *) (DebugHeader + 1); | |
| if (RegisterRead->Index < SOFT_DEBUGGER_REGISTER_OTHERS_BASE) { | |
| Data8 = RegisterRead->Length; | |
| RegisterBuffer = ArchReadRegisterBuffer (CpuContext, RegisterRead->Index, RegisterRead->Offset, &Data8); | |
| Status = SendDataResponsePacket (CpuContext, RegisterBuffer, Data8); | |
| break; | |
| } | |
| if (RegisterRead->Index <= SOFT_DEBUGGER_REGISTER_TSS_LIM) { | |
| ReadRegisterGroupSegLim (CpuContext, &RegisterGroupSegLim); | |
| DataN = * ((UINTN *) &RegisterGroupSegLim + (RegisterRead->Index - SOFT_DEBUGGER_REGISTER_CS_LIM)); | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) &DataN, (UINT16) sizeof (UINTN)); | |
| } else if (RegisterRead->Index <= SOFT_DEBUGGER_REGISTER_TSS_BAS) { | |
| ReadRegisterGroupSegBase (CpuContext, &RegisterGroupSegBase); | |
| DataN = * ((UINTN *) &RegisterGroupSegBase + (RegisterRead->Index - SOFT_DEBUGGER_REGISTER_CS_BAS)); | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) &DataN, (UINT16) sizeof (UINTN)); | |
| } else if (RegisterRead->Index < SOFT_DEBUGGER_REGISTER_IDT_LIM) { | |
| Data64 = ReadRegisterSelectorByIndex (CpuContext, RegisterRead->Index); | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) &Data64, (UINT16) sizeof (UINT64)); | |
| } else { | |
| switch (RegisterRead->Index) { | |
| case SOFT_DEBUGGER_REGISTER_IDT_LIM: | |
| DataN = (UINTN) (CpuContext->Idtr[0] & 0xffff); | |
| SendDataResponsePacket (CpuContext, (UINT8 *) &DataN, (UINT16) sizeof (UINTN)); | |
| break; | |
| case SOFT_DEBUGGER_REGISTER_GDT_LIM: | |
| DataN = (UINTN) (CpuContext->Gdtr[0] & 0xffff); | |
| SendDataResponsePacket (CpuContext, (UINT8 *) &DataN, (UINT16) sizeof (UINTN)); | |
| break; | |
| case SOFT_DEBUGGER_REGISTER_IDT_BAS: | |
| DataN = (UINTN) RShiftU64 (CpuContext->Idtr[0], 16); | |
| DataN |= (UINTN) LShiftU64 (CpuContext->Idtr[1], (UINT16) (sizeof (UINTN) * 8 - 16)); | |
| SendDataResponsePacket (CpuContext, (UINT8 *) &DataN, (UINT16) sizeof (UINTN)); | |
| break; | |
| case SOFT_DEBUGGER_REGISTER_GDT_BAS: | |
| DataN = (UINTN) RShiftU64 (CpuContext->Gdtr[0], 16); | |
| DataN |= (UINTN) LShiftU64 (CpuContext->Gdtr[1], (UINT16) (sizeof (UINTN) * 8 - 16)); | |
| SendDataResponsePacket (CpuContext, (UINT8 *) &DataN, (UINT16) sizeof (UINTN)); | |
| break; | |
| } | |
| } | |
| break; | |
| case DEBUG_COMMAND_WRITE_REGISTER: | |
| RegisterWrite = (DEBUG_DATA_WRITE_REGISTER *) (DebugHeader + 1); | |
| ArchWriteRegisterBuffer (CpuContext, RegisterWrite->Index, RegisterWrite->Offset, RegisterWrite->Length, (UINT8 *)&RegisterWrite->Value); | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| break; | |
| case DEBUG_COMMAND_ARCH_MODE: | |
| Data8 = DEBUG_ARCH_SYMBOL; | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) &Data8, (UINT16) sizeof (UINT8)); | |
| break; | |
| case DEBUG_COMMAND_READ_MSR: | |
| MsrRegisterRead = (DEBUG_DATA_READ_MSR *) (DebugHeader + 1); | |
| Data64 = AsmReadMsr64 (MsrRegisterRead->Index); | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) &Data64, (UINT16) sizeof (UINT64)); | |
| break; | |
| case DEBUG_COMMAND_WRITE_MSR: | |
| MsrRegisterWrite = (DEBUG_DATA_WRITE_MSR *) (DebugHeader + 1); | |
| AsmWriteMsr64 (MsrRegisterWrite->Index, MsrRegisterWrite->Value); | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| break; | |
| case DEBUG_COMMAND_READ_REGISTER_GROUP: | |
| Data8 = *(UINT8 *) (DebugHeader + 1); | |
| Status = ArchReadRegisterGroup (CpuContext, Data8); | |
| break; | |
| case DEBUG_COMMAND_SET_DEBUG_FLAG: | |
| Data32 = *(UINT32 *) (DebugHeader + 1); | |
| SetDebugFlag (Data32); | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| break; | |
| case DEBUG_COMMAND_GET_REVISION: | |
| DebugAgentRevision.Revision = DEBUG_AGENT_REVISION; | |
| DebugAgentRevision.Capabilities = DEBUG_AGENT_CAPABILITIES; | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) &DebugAgentRevision, (UINT16) sizeof (DEBUG_DATA_RESPONSE_GET_REVISION)); | |
| break; | |
| case DEBUG_COMMAND_GET_EXCEPTION: | |
| Exception.ExceptionNum = (UINT8) Vector; | |
| Exception.ExceptionData = 0; | |
| Status = SendDataResponsePacket (CpuContext, (UINT8 *) &Exception, (UINT16) sizeof (DEBUG_DATA_RESPONSE_GET_EXCEPTION)); | |
| break; | |
| case DEBUG_COMMAND_SET_VIEWPOINT: | |
| Data32 = *(UINT32 *) (DebugHeader + 1); | |
| if (MultiProcessorDebugSupport) { | |
| if (IsCpuStopped (Data32)) { | |
| SetDebugViewPoint (Data32); | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| } else { | |
| // | |
| // If CPU is not halted | |
| // | |
| SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED); | |
| } | |
| } else if (Data32 == 0) { | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| } else { | |
| SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED); | |
| } | |
| break; | |
| case DEBUG_COMMAND_GET_VIEWPOINT: | |
| Data32 = mDebugMpContext.ViewPointIndex; | |
| SendDataResponsePacket(CpuContext, (UINT8 *) &Data32, (UINT16) sizeof (UINT32)); | |
| break; | |
| default: | |
| SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED); | |
| break; | |
| } | |
| if (Status == RETURN_UNSUPPORTED) { | |
| SendAckPacket (DEBUG_COMMAND_NOT_SUPPORTED); | |
| } else if (Status != RETURN_SUCCESS) { | |
| SendAckPacket (DEBUG_COMMAND_ABORT); | |
| } | |
| ReleaseDebugPortControl (); | |
| CpuPause (); | |
| } | |
| } | |
| /** | |
| C function called in interrupt handler. | |
| @param[in] Vector Vector value of exception or interrutp. | |
| @param[in] CpuContext Pointer to save CPU context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| InterruptProcess ( | |
| IN UINT32 Vector, | |
| IN DEBUG_CPU_CONTEXT *CpuContext | |
| ) | |
| { | |
| UINT8 InputCharacter; | |
| UINT8 BreakCause; | |
| UINTN SavedEip; | |
| BOOLEAN BreakReceived; | |
| UINT32 ProcessorIndex; | |
| UINT32 CurrentDebugTimerInitCount; | |
| DEBUG_PORT_HANDLE Handle; | |
| UINT8 Data8; | |
| Handle = GetDebugPortHandle(); | |
| ProcessorIndex = 0; | |
| BreakReceived = FALSE; | |
| if (MultiProcessorDebugSupport) { | |
| ProcessorIndex = GetProcessorIndex (); | |
| while (mDebugMpContext.RunCommandSet); | |
| } | |
| switch (Vector) { | |
| case DEBUG_INT1_VECTOR: | |
| case DEBUG_INT3_VECTOR: | |
| BreakCause = GetBreakCause (Vector, CpuContext); | |
| if (BreakCause == DEBUG_DATA_BREAK_CAUSE_SYSTEM_RESET) { | |
| // | |
| // Init break, if no ack received after 200ms, return | |
| // | |
| SendAckPacket (DEBUG_COMMAND_INIT_BREAK); | |
| if (WaitForAckPacketOK (200 * 1000, &BreakReceived) != RETURN_SUCCESS) { | |
| break; | |
| } | |
| SetHostConnectedFlag (); | |
| CommandCommunication (Vector, CpuContext, BreakReceived); | |
| } else if (BreakCause == DEBUG_DATA_BREAK_CAUSE_STEPPING) { | |
| // | |
| // Stepping is finished, send Ack package. | |
| // | |
| if (MultiProcessorDebugSupport) { | |
| mDebugMpContext.BreakAtCpuIndex = ProcessorIndex; | |
| } | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| CommandCommunication (Vector, CpuContext, BreakReceived); | |
| } else if (BreakCause == DEBUG_DATA_BREAK_CAUSE_MEMORY_READY) { | |
| // | |
| // Memory is ready | |
| // | |
| SendAckPacket (DEBUG_COMMAND_MEMORY_READY); | |
| WaitForAckPacketOK (0, &BreakReceived); | |
| CommandCommunication (Vector, CpuContext, BreakReceived); | |
| } else { | |
| if (BreakCause == DEBUG_DATA_BREAK_CAUSE_IMAGE_LOAD || BreakCause == DEBUG_DATA_BREAK_CAUSE_IMAGE_UNLOAD) { | |
| // | |
| // Set AL to DEBUG_AGENT_IMAGE_CONTINUE | |
| // | |
| Data8 = DEBUG_AGENT_IMAGE_CONTINUE; | |
| ArchWriteRegisterBuffer (CpuContext, SOFT_DEBUGGER_REGISTER_AX, 0, 1, &Data8); | |
| if (!IsHostConnected ()) { | |
| // | |
| // If HOST is not connected, return | |
| // | |
| break; | |
| } | |
| } | |
| AcquireDebugPortControl (); | |
| if (MultiProcessorDebugSupport) { | |
| if(!IsAllCpuRunning ()) { | |
| // | |
| // If other processors have been stopped | |
| // | |
| SetCpuBreakFlagByIndex (ProcessorIndex, TRUE); | |
| } else { | |
| // | |
| // If no any processor was stopped, try to halt other processors | |
| // | |
| HaltOtherProcessors (ProcessorIndex); | |
| SendAckPacket (DEBUG_COMMAND_BREAK_POINT); | |
| WaitForAckPacketOK (0, &BreakReceived); | |
| } | |
| } else { | |
| SendAckPacket (DEBUG_COMMAND_BREAK_POINT); | |
| WaitForAckPacketOK (0, &BreakReceived); | |
| } | |
| ReleaseDebugPortControl (); | |
| if (Vector == DEBUG_INT3_VECTOR) { | |
| // | |
| // go back address located "0xCC" | |
| // | |
| CpuContext->Eip--; | |
| SavedEip = CpuContext->Eip; | |
| CommandCommunication (Vector, CpuContext, BreakReceived); | |
| if ((SavedEip == CpuContext->Eip) && | |
| (*(UINT8 *) (UINTN) CpuContext->Eip == DEBUG_SW_BREAKPOINT_SYMBOL)) { | |
| // | |
| // If this is not a software breakpoint set by HOST, | |
| // restore EIP | |
| // | |
| CpuContext->Eip++; | |
| } | |
| } else { | |
| CommandCommunication (Vector, CpuContext, BreakReceived); | |
| } | |
| } | |
| break; | |
| case DEBUG_TIMER_VECTOR: | |
| if (MultiProcessorDebugSupport) { | |
| if (IsBsp (ProcessorIndex)) { | |
| // | |
| // If current processor is BSP, check Apic timer's init count if changed, | |
| // it may be re-written when switching BSP. | |
| // If it changed, re-initialize debug timer | |
| // | |
| CurrentDebugTimerInitCount = GetApicTimerInitCount (); | |
| if (mDebugMpContext.DebugTimerInitCount != CurrentDebugTimerInitCount) { | |
| InitializeDebugTimer (); | |
| } | |
| } | |
| if (!IsBsp (ProcessorIndex) || mDebugMpContext.IpiSentByAp) { | |
| // | |
| // If current processor is not BSP or this is one IPI sent by AP | |
| // | |
| if (mDebugMpContext.BreakAtCpuIndex != (UINT32) (-1)) { | |
| CommandCommunication (Vector, CpuContext, FALSE); | |
| } | |
| // | |
| // Clear EOI before exiting interrupt process routine. | |
| // | |
| SendApicEoi (); | |
| break; | |
| } | |
| } | |
| // | |
| // Only BSP could run here | |
| // | |
| AcquireDebugPortControl (); | |
| while (DebugPortPollBuffer (Handle)) { | |
| // | |
| // If there is data in debug port, will check whether it is break-in symbol, | |
| // If yes, go into communication mode with HOST. | |
| // If no, exit interrupt process. | |
| // | |
| DebugPortReadBuffer (Handle, &InputCharacter, 1, 0); | |
| if (InputCharacter == DEBUG_STARTING_SYMBOL_BREAK) { | |
| SendAckPacket (DEBUG_COMMAND_OK); | |
| if (MultiProcessorDebugSupport) { | |
| if(FindCpuNotRunning () != -1) { | |
| SetCpuBreakFlagByIndex (ProcessorIndex, TRUE); | |
| } else { | |
| HaltOtherProcessors (ProcessorIndex); | |
| } | |
| } | |
| ReleaseDebugPortControl (); | |
| CommandCommunication (Vector, CpuContext, BreakReceived); | |
| AcquireDebugPortControl (); | |
| break; | |
| } | |
| } | |
| // | |
| // Clear EOI before exiting interrupt process routine. | |
| // | |
| SendApicEoi (); | |
| ReleaseDebugPortControl (); | |
| break; | |
| default: | |
| if (Vector <= DEBUG_EXCEPT_SIMD) { | |
| AcquireDebugPortControl (); | |
| if (MultiProcessorDebugSupport) { | |
| if(FindCpuNotRunning () != -1) { | |
| SetCpuBreakFlagByIndex (ProcessorIndex, TRUE); | |
| } else { | |
| HaltOtherProcessors (ProcessorIndex); | |
| } | |
| } | |
| SendAckPacket (DEBUG_COMMAND_BREAK_POINT); | |
| WaitForAckPacketOK (0, &BreakReceived); | |
| ReleaseDebugPortControl (); | |
| CommandCommunication (Vector, CpuContext, BreakReceived); | |
| } | |
| break; | |
| } | |
| if (MultiProcessorDebugSupport) { | |
| // | |
| // Clear flag and wait for all processors run here | |
| // | |
| SetIpiSentByApFlag (FALSE); | |
| while (mDebugMpContext.RunCommandSet); | |
| } | |
| return; | |
| } | |