//---------------------------------------------------------------------------- // // KD hard-line communication support. // // Copyright (C) Microsoft Corporation, 1999-2002. // //---------------------------------------------------------------------------- #include "ntsdp.hpp" #include #include #include #define THROTTLE_WRITES 0 #define DBG_SYNCH 0 struct KD_FILE_ASSOC { LIST_ENTRY List; PWSTR From; PSTR To; }; ULONG g_LastProcessorToPrint = (ULONG) -1; CHAR g_PrintBuf[PACKET_MAX_SIZE]; PCSTR g_DbgKdTransportNames[] = { "COM", "1394" }; // This log is for debugging the protocol so leave it // a simple global for easy examination. ULONG g_PacketLogIndex; ULONG64 g_PacketLog[16]; #define PACKET_LOG_SIZE (sizeof(g_PacketLog) / sizeof(g_PacketLog[0])) UCHAR DbgKdTransport::s_BreakinPacket[1] = { BREAKIN_PACKET_BYTE }; UCHAR DbgKdTransport::s_PacketTrailingByte[1] = { PACKET_TRAILING_BYTE }; UCHAR DbgKdTransport::s_PacketLeader[4] = { PACKET_LEADER_BYTE, PACKET_LEADER_BYTE, PACKET_LEADER_BYTE, PACKET_LEADER_BYTE }; UCHAR DbgKdTransport::s_Packet[PACKET_MAX_MANIP_SIZE]; KD_PACKET DbgKdTransport::s_PacketHeader; UCHAR DbgKdTransport::s_SavedPacket[PACKET_MAX_MANIP_SIZE]; KD_PACKET DbgKdTransport::s_SavedPacketHeader; #define COPYSE(p64,p32,f) p64->f = (ULONG64)(LONG64)(LONG)p32->f __inline void DbgkdGetVersion32To64( IN PDBGKD_GET_VERSION32 vs32, OUT PDBGKD_GET_VERSION64 vs64, OUT PKDDEBUGGER_DATA64 dd64 ) { vs64->MajorVersion = vs32->MajorVersion; vs64->MinorVersion = vs32->MinorVersion; vs64->ProtocolVersion = vs32->ProtocolVersion; vs64->Flags = vs32->Flags; vs64->MachineType = vs32->MachineType; COPYSE(vs64,vs32,PsLoadedModuleList); COPYSE(vs64,vs32,DebuggerDataList); COPYSE(vs64,vs32,KernBase); COPYSE(dd64,vs32,KernBase); COPYSE(dd64,vs32,PsLoadedModuleList); dd64->ThCallbackStack = vs32->ThCallbackStack; dd64->NextCallback = vs32->NextCallback; dd64->FramePointer = vs32->FramePointer; COPYSE(dd64,vs32,KiCallUserMode); COPYSE(dd64,vs32,KeUserCallbackDispatcher); COPYSE(dd64,vs32,BreakpointWithStatus); } void OutputIo(PSTR Format, PVOID _Buffer, ULONG Request, ULONG Done) { ULONG i, Chunk; PUCHAR Buffer = (PUCHAR)_Buffer; dprintf(Format, Done, Request); while (Done > 0) { Chunk = min(Done, 16); Done -= Chunk; dprintf(" "); for (i = 0; i < Chunk; i++) { dprintf(" %02X", *Buffer++); } dprintf("\n"); } } //---------------------------------------------------------------------------- // // DbgKdTransport. // //---------------------------------------------------------------------------- DbgKdTransport::DbgKdTransport(ConnLiveKernelTargetInfo* Target) { m_Refs = 1; m_Target = Target; m_ReadOverlapped.hEvent = NULL; m_WriteOverlapped.hEvent = NULL; InitializeListHead(&m_KdFiles); InitKdFileAssoc(); Restart(); } DbgKdTransport::~DbgKdTransport(void) { if (m_ReadOverlapped.hEvent != NULL) { CloseHandle(m_ReadOverlapped.hEvent); } if (m_WriteOverlapped.hEvent != NULL) { CloseHandle(m_WriteOverlapped.hEvent); } while (!IsListEmpty(&m_KdFiles)) { CloseKdFile(CONTAINING_RECORD(m_KdFiles.Flink, KD_FILE, List)); } ClearKdFileAssoc(); } ULONG DbgKdTransport::GetNumberParameters(void) { return 1; } void DbgKdTransport::GetParameter(ULONG Index, PSTR Name, ULONG NameSize, PSTR Value, ULONG ValueSize) { switch(Index) { case 0: if (m_OutputIo) { CopyString(Name, "OutputIo", NameSize); PrintString(Value, ValueSize, "0x%x", m_OutputIo); } break; } } void DbgKdTransport::ResetParameters(void) { m_OutputIo = 0; } BOOL DbgKdTransport::SetParameter(PCSTR Name, PCSTR Value) { if (!_strcmpi(Name, "OutputIo")) { if (!Value || sscanf(Value, "%i", &m_OutputIo) != 1) { ErrOut("OutputIo requires a numeric value\n"); return FALSE; } } else { ErrOut("%s parameters: %s is not a valid parameter\n", m_Name, Name); return FALSE; } return TRUE; } void DbgKdTransport::Restart(void) { // // Reinitialize per-connection values. // while (!IsListEmpty(&m_KdFiles)) { CloseKdFile(CONTAINING_RECORD(m_KdFiles.Flink, KD_FILE, List)); } m_PacketsRead = 0; m_BytesRead = 0; m_PacketsWritten = 0; m_BytesWritten = 0; m_PacketExpected = INITIAL_PACKET_ID; m_NextPacketToSend = INITIAL_PACKET_ID; m_WaitingThread = 0; m_AllowInitialBreak = TRUE; m_Resync = TRUE; m_BreakIn = FALSE; m_SyncBreakIn = FALSE; m_ValidUnaccessedPacket = FALSE; } void DbgKdTransport::OutputInfo(void) { char Params[2 * (MAX_PARAM_NAME + MAX_PARAM_VALUE)]; GetParameters(Params, sizeof(Params)); dprintf("Transport %s\n", Params); dprintf("Packets read: %u, bytes read %I64u\n", m_PacketsRead, m_BytesRead); dprintf("Packets written: %u, bytes written %I64u\n", m_PacketsWritten, m_BytesWritten); } HRESULT DbgKdTransport::Initialize(void) { HRESULT Status; // // Create the events used by the overlapped structures for the // read and write. // if ((Status = CreateOverlappedPair(&m_ReadOverlapped, &m_WriteOverlapped)) != S_OK) { ErrOut("Unable to create overlapped info, %s\n", FormatStatusCode(Status)); } return Status; } void DbgKdTransport::CycleSpeed(void) { WarnOut("KD transport cannot change speeds\n"); } HRESULT DbgKdTransport::ReadTargetPhysicalMemory( IN ULONG64 MemoryOffset, IN PVOID Buffer, IN ULONG SizeofBuffer, IN PULONG BytesRead ) { WarnOut("Not valid KD transport operation\n"); return E_UNEXPECTED; } ULONG DbgKdTransport::HandleDebugIo(PDBGKD_DEBUG_IO Packet) { ULONG ReadStatus = DBGKD_WAIT_AGAIN; switch(Packet->ApiNumber) { case DbgKdPrintStringApi: HandlePrint(Packet->Processor, (PSTR)(Packet + 1), (SHORT)Packet->u.PrintString.LengthOfString, DEBUG_OUTPUT_DEBUGGEE); break; case DbgKdGetStringApi: HandlePromptString(Packet); break; default: KdOut("READ: Received INVALID DEBUG_IO packet type %x.\n", Packet->ApiNumber); ReadStatus = DBGKD_WAIT_RESEND; break; } return ReadStatus; } ULONG DbgKdTransport::HandleTraceIo(PDBGKD_TRACE_IO Packet) { ULONG ReadStatus = DBGKD_WAIT_AGAIN; switch(Packet->ApiNumber) { case DbgKdPrintTraceApi: HandlePrintTrace(Packet->Processor, (PUCHAR)(Packet + 1), (USHORT)Packet->u.PrintTrace.LengthOfData, DEBUG_OUTPUT_DEBUGGEE); break; default: KdOut("READ: Received INVALID TRACE_IO packet type %x.\n", Packet->ApiNumber); ReadStatus = DBGKD_WAIT_RESEND; break; } return ReadStatus; } ULONG DbgKdTransport::HandleControlRequest(PDBGKD_CONTROL_REQUEST Packet) { ULONG ReadStatus = DBGKD_WAIT_AGAIN; switch(Packet->ApiNumber) { case DbgKdRequestHardwareBp: Packet->u.RequestBreakpoint.Available = FALSE; WritePacket(Packet, sizeof(*Packet), PACKET_TYPE_KD_CONTROL_REQUEST, NULL, 0); break; case DbgKdReleaseHardwareBp: Packet->u.ReleaseBreakpoint.Released = TRUE; WritePacket(Packet, sizeof(*Packet), PACKET_TYPE_KD_CONTROL_REQUEST, NULL, 0); break; default: KdOut("READ: Received INVALID CONTROL_REQUEST packet type %x.\n", Packet->ApiNumber); ReadStatus = DBGKD_WAIT_RESEND; break; } return ReadStatus; } ULONG DbgKdTransport::HandleFileIo(PDBGKD_FILE_IO Packet) { KD_FILE* File = NULL; PVOID ExtraData = NULL; USHORT ExtraDataLength = 0; LARGE_INTEGER FilePtr; // Reenter the engine lock to protect the file list. RESUME_ENGINE(); switch(Packet->ApiNumber) { case DbgKdCreateFileApi: Packet->Status = CreateKdFile((PWSTR)(Packet + 1), Packet->u.CreateFile.DesiredAccess, Packet->u.CreateFile.FileAttributes, Packet->u.CreateFile.ShareAccess, Packet->u.CreateFile.CreateDisposition, Packet->u.CreateFile.CreateOptions, &File, &Packet->u.CreateFile.Length); Packet->u.CreateFile.Handle = (ULONG_PTR)File; KdOut("KdFile request for '%ws' returns %08X\n", (PWSTR)(Packet + 1), Packet->Status); break; case DbgKdReadFileApi: File = TranslateKdFileHandle(Packet->u.ReadFile.Handle); if (File == NULL || Packet->u.ReadFile.Length > PACKET_MAX_SIZE - sizeof(*Packet)) { Packet->Status = STATUS_INVALID_PARAMETER; break; } FilePtr.QuadPart = Packet->u.ReadFile.Offset; if (SetFilePointer(File->Handle, FilePtr.LowPart, &FilePtr.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError()) { Packet->Status = STATUS_END_OF_FILE; break; } if (!ReadFile(File->Handle, Packet + 1, Packet->u.ReadFile.Length, &Packet->u.ReadFile.Length, NULL)) { Packet->Status = STATUS_UNSUCCESSFUL; } else { dprintf("."); Packet->Status = STATUS_SUCCESS; ExtraData = Packet + 1; ExtraDataLength = (USHORT)Packet->u.ReadFile.Length; } break; case DbgKdWriteFileApi: File = TranslateKdFileHandle(Packet->u.WriteFile.Handle); if (File == NULL || Packet->u.WriteFile.Length > PACKET_MAX_SIZE - sizeof(*Packet)) { Packet->Status = STATUS_INVALID_PARAMETER; break; } FilePtr.QuadPart = Packet->u.WriteFile.Offset; if (SetFilePointer(File->Handle, FilePtr.LowPart, &FilePtr.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError()) { Packet->Status = STATUS_END_OF_FILE; break; } if (!WriteFile(File->Handle, Packet + 1, Packet->u.WriteFile.Length, &Packet->u.WriteFile.Length, NULL)) { Packet->Status = STATUS_UNSUCCESSFUL; } else { dprintf("."); Packet->Status = STATUS_SUCCESS; } break; case DbgKdCloseFileApi: File = TranslateKdFileHandle(Packet->u.CloseFile.Handle); if (File != NULL) { // Finish line of progress dots. dprintf("\n"); CloseKdFile(File); Packet->Status = STATUS_SUCCESS; } else { Packet->Status = STATUS_INVALID_PARAMETER; } break; default: KdOut("READ: Received INVALID FILE_IO packet type %x.\n", Packet->ApiNumber); SUSPEND_ENGINE(); return DBGKD_WAIT_RESEND; } // // Send response data. // WritePacket(Packet, sizeof(*Packet), PACKET_TYPE_KD_FILE_IO, ExtraData, ExtraDataLength); SUSPEND_ENGINE(); return DBGKD_WAIT_AGAIN; } ULONG DbgKdTransport::WaitForPacket( IN USHORT PacketType, OUT PVOID Packet ) { ULONG InvPacketRetry = 0; // Packets can only be read when the kernel transport // is not in use. if (m_WaitingThread != 0 && m_WaitingThread != GetCurrentThreadId()) { ErrOut("Kernel transport in use, packet read failed\n"); return DBGKD_WAIT_FAILED; } if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { KdOut("READ: Wait for ACK packet with id = %lx\n", m_NextPacketToSend); } else { KdOut("READ: Wait for type %x packet exp id = %lx\n", PacketType, m_PacketExpected); } g_PacketLog[g_PacketLogIndex++ & (PACKET_LOG_SIZE - 1)] = ((ULONG64)PacketType << 32); if (PacketType != PACKET_TYPE_KD_ACKNOWLEDGE) { if (m_ValidUnaccessedPacket) { KdOut("READ: Grab packet from buffer.\n"); goto ReadBuffered; } } ReadContents: for (;;) { ULONG ReadStatus = ReadPacketContents(PacketType); // // If we read an internal packet such as IO or Resend, then // handle it and continue waiting. // if (ReadStatus == DBGKD_WAIT_PACKET) { m_PacketsRead++; switch(s_PacketHeader.PacketType) { case PACKET_TYPE_KD_DEBUG_IO: ReadStatus = HandleDebugIo((PDBGKD_DEBUG_IO)s_Packet); break; case PACKET_TYPE_KD_TRACE_IO: ReadStatus = HandleTraceIo((PDBGKD_TRACE_IO)s_Packet); break; case PACKET_TYPE_KD_CONTROL_REQUEST: ReadStatus = HandleControlRequest((PDBGKD_CONTROL_REQUEST)s_Packet); break; case PACKET_TYPE_KD_FILE_IO: ReadStatus = HandleFileIo((PDBGKD_FILE_IO)s_Packet); break; } } else if (ReadStatus == DBGKD_WAIT_ACK) { m_PacketsRead++; // If we're waiting for an ack we're done, // otherwise the communication is confused // so ask for a resend. if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { return DBGKD_WAIT_ACK; } else { KdOut("READ: Received ACK while waiting for type %d\n", PacketType); ReadStatus = DBGKD_WAIT_RESEND; } } if (ReadStatus == DBGKD_WAIT_PACKET) { // If we're waiting for an ack and received // a normal packet leave it in the buffer // and record the fact that we have one // stored. Consider it an ack and return. if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { m_ValidUnaccessedPacket = TRUE; KdOut("READ: Packet Read ahead.\n"); FlushCallbacks(); return DBGKD_WAIT_ACK; } // We're waiting for a data packet and we // just got one so process it. break; } else if (ReadStatus == DBGKD_WAIT_RESEND) { // If the other end didn't wait for an // ack then we can't ask for a resend. if (!m_AckWrites) { return DBGKD_WAIT_FAILED; } WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { return DBGKD_WAIT_ACK; } KdOut("READ: Ask for resend.\n"); } else if (ReadStatus == DBGKD_WAIT_AGAIN) { // Internal packets count as acknowledgements, // so if we processed one while waiting for an // ack consider things done. if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { return DBGKD_WAIT_ACK; } } else { return ReadStatus; } } ReadBuffered: // // Check PacketType is what we are waiting for. // if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { if (s_PacketHeader.PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { m_Target->m_KdApi64 = TRUE; } else if (s_PacketHeader.PacketType == PACKET_TYPE_KD_STATE_CHANGE32) { PacketType = PACKET_TYPE_KD_STATE_CHANGE32; m_Target->m_KdApi64 = FALSE; } KdOut("READ: Packet type = %x, KdApi64 = %x\n", s_PacketHeader.PacketType, m_Target->m_KdApi64); } if (PacketType != s_PacketHeader.PacketType) { KdOut("READ: Unexpected Packet type %x (Acked). " "Expecting Packet type %x\n", s_PacketHeader.PacketType, PacketType); if (m_InvPacketRetryLimit > 0 && ++InvPacketRetry >= m_InvPacketRetryLimit) { return DBGKD_WAIT_FAILED; } goto ReadContents; } if (!m_Target->m_KdApi64 && PacketType == PACKET_TYPE_KD_STATE_MANIPULATE) { DBGKD_MANIPULATE_STATE64 Packet64; DWORD AdditionalDataSize; DbgkdManipulateState32To64((PDBGKD_MANIPULATE_STATE32)&s_Packet, &Packet64, &AdditionalDataSize); if (Packet64.ApiNumber == DbgKdGetVersionApi) { DbgkdGetVersion32To64(&((PDBGKD_MANIPULATE_STATE32)&s_Packet)-> u.GetVersion32, &Packet64.u.GetVersion64, &m_Target->m_KdDebuggerData); } else if (AdditionalDataSize) { // // Move the trailing data to make room for the larger packet header // MoveMemory(s_Packet + sizeof(DBGKD_MANIPULATE_STATE64), s_Packet + sizeof(DBGKD_MANIPULATE_STATE32), AdditionalDataSize); } *(PDBGKD_MANIPULATE_STATE64)s_Packet = Packet64; } *(PVOID *)Packet = &s_Packet; m_ValidUnaccessedPacket = FALSE; return DBGKD_WAIT_PACKET; } VOID DbgKdTransport::WriteBreakInPacket(VOID) { DWORD BytesWritten; BOOL Succ; KdOut("Send Break in ...\n"); FlushCallbacks(); for (;;) { Succ = Write(&s_BreakinPacket[0], sizeof(s_BreakinPacket), &BytesWritten); if (Succ && BytesWritten == sizeof(s_BreakinPacket)) { break; } // Avoid consuming 100% of the CPU when spinning. Sleep(10); } m_BreakIn = FALSE; m_PacketsWritten++; } VOID DbgKdTransport::WriteControlPacket( IN USHORT PacketType, IN ULONG PacketId OPTIONAL ) /*++ Routine Description: This function writes a control packet to target machine. N.B. a CONTROL Packet header is sent with the following information: PacketLeader - indicates it's a control packet PacketType - indicates the type of the control packet ByteCount - aways zero to indicate no data following the header PacketId - Valid ONLY for PACKET_TYPE_KD_ACKNOWLEDGE to indicate which packet is acknowledged. Arguments: PacketType - Supplies the type of the control packet. PacketId - Supplies the PacketId. Used by Acknowledge packet only. Return Value: None. --*/ { DWORD BytesWritten; BOOL Succ; KD_PACKET Packet; DBG_ASSERT( (m_Target->m_KdMaxPacketType == 0 && PacketType < PACKET_TYPE_MAX) || (m_Target->m_KdMaxPacketType > 0 && PacketType < m_Target->m_KdMaxPacketType) ); Packet.PacketLeader = CONTROL_PACKET_LEADER; Packet.ByteCount = 0; Packet.PacketType = PacketType; if (PacketId) { Packet.PacketId = PacketId; } else { Packet.PacketId = 0; } Packet.Checksum = 0; for (;;) { // Write the control packet header. We need this // to be sent so retry until the write succeeds. Succ = Write(&Packet, sizeof(Packet), &BytesWritten); if (Succ && BytesWritten == sizeof(Packet)) { break; } // Avoid consuming 100% of the CPU when spinning. Sleep(10); } m_PacketsWritten++; } VOID DbgKdTransport::WriteDataPacket( IN PVOID PacketData, IN USHORT PacketDataLength, IN USHORT PacketType, IN PVOID MorePacketData OPTIONAL, IN USHORT MorePacketDataLength OPTIONAL, IN BOOL NoAck ) { KD_PACKET Packet; USHORT TotalBytesToWrite; DBGKD_MANIPULATE_STATE32 m32; PVOID ConvertedPacketData = NULL; DBG_ASSERT( (m_Target->m_KdMaxPacketType == 0 && PacketType < PACKET_TYPE_MAX) || (m_Target->m_KdMaxPacketType > 0 && PacketType < m_Target->m_KdMaxPacketType) ); // Packets can only be written when the kernel transport // is not in use. if (m_WaitingThread != 0 && m_WaitingThread != GetCurrentThreadId()) { ErrOut("Kernel transport in use, packet write failed\n"); return; } KdOut("WRITE: Write type %x packet id= %lx.\n", PacketType, m_NextPacketToSend); if (!m_Target->m_KdApi64 && PacketType == PACKET_TYPE_KD_STATE_MANIPULATE) { PacketDataLength = (USHORT) DbgkdManipulateState64To32((PDBGKD_MANIPULATE_STATE64)PacketData, &m32); PacketData = (PVOID)&m32; if (m32.ApiNumber == DbgKdWriteBreakPointExApi) { ConvertedPacketData = malloc(MorePacketDataLength / 2); if (!ConvertedPacketData) { ErrOut("Failed to allocate Packet Data\n"); return; } ConvertQwordsToDwords((PULONG64)PacketData, (PULONG)ConvertedPacketData, MorePacketDataLength / 8); MorePacketData = ConvertedPacketData; MorePacketDataLength /= 2; } } if ( ARGUMENT_PRESENT(MorePacketData) ) { TotalBytesToWrite = PacketDataLength + MorePacketDataLength; Packet.Checksum = ComputeChecksum((PUCHAR)MorePacketData, MorePacketDataLength); } else { TotalBytesToWrite = PacketDataLength; Packet.Checksum = 0; } Packet.Checksum += ComputeChecksum((PUCHAR)PacketData, PacketDataLength); Packet.PacketLeader = PACKET_LEADER; Packet.ByteCount = TotalBytesToWrite; Packet.PacketType = PacketType; g_PacketLog[g_PacketLogIndex++ & (PACKET_LOG_SIZE - 1)] = ((ULONG64)0xF << 60) | ((ULONG64)PacketType << 32) | TotalBytesToWrite; for (;;) { Packet.PacketId = m_NextPacketToSend; if (WritePacketContents(&Packet, PacketData, PacketDataLength, MorePacketData, MorePacketDataLength, NoAck) == DBGKD_WRITE_PACKET) { m_PacketsWritten++; break; } } if (ConvertedPacketData) { free(ConvertedPacketData); } } ULONG DbgKdTransport::ComputeChecksum( IN PUCHAR Buffer, IN ULONG Length ) { ULONG Checksum = 0; while (Length > 0) { Checksum = Checksum + (ULONG)*Buffer++; Length--; } return Checksum; } void DbgKdTransport::HandlePrint(IN ULONG Processor, IN PCSTR String, IN USHORT StringLength, IN ULONG Mask) { DWORD i; DWORD j; CHAR c; PSTR d; DBG_ASSERT(StringLength < PACKET_MAX_SIZE - 2); // This routine can be called during a wait when the // engine lock isn't held and can also be called when // the lock is held. RESUME handles both of these // cases so that the lock is reacquired or reentered. RESUME_ENGINE(); if (m_Target->m_NumProcessors > 1 && Processor != g_LastProcessorToPrint) { g_LastProcessorToPrint = Processor; MaskOut(Mask, "%d:", Processor); } StartOutLine(Mask, OUT_LINE_NO_PREFIX); // // Add the original data to the print buffer. // d = g_PrintBuf; for (i = 0; i < StringLength ; i++) { c = *(String + i); if ( c == '\n' ) { g_LastProcessorToPrint = -1; *d++ = '\n'; *d++ = '\r'; } else { if ( c ) { *d++ = c; } } } j = (DWORD)(d - g_PrintBuf); // // print the string. // MaskOut(Mask, "%*.*s", j, j, g_PrintBuf); SUSPEND_ENGINE(); } void DbgKdTransport::HandlePromptString(IN PDBGKD_DEBUG_IO IoMessage) { PSTR IoData; DWORD j; // This routine can be called during a wait when the // engine lock isn't held and can also be called when // the lock is held. RESUME handles both of these // cases so that the lock is reacquired or reentered. RESUME_ENGINE(); IoData = (PSTR)(IoMessage + 1); HandlePrint(IoMessage->Processor, IoData, (USHORT)IoMessage->u.GetString.LengthOfPromptString, DEBUG_OUTPUT_DEBUGGEE_PROMPT); // // read the prompt data // j = GetInput(NULL, IoData, IoMessage->u.GetString.LengthOfStringRead, GETIN_LOG_INPUT_LINE); if (j == 0) { j = IoMessage->u.GetString.LengthOfStringRead; memset(IoData, 0, j); } g_LastProcessorToPrint = -1; if ( j < (USHORT)IoMessage->u.GetString.LengthOfStringRead ) { IoMessage->u.GetString.LengthOfStringRead = j; } SUSPEND_ENGINE(); // // Send data to the debugger-target // WritePacket(IoMessage, sizeof(*IoMessage), PACKET_TYPE_KD_DEBUG_IO, IoData, (USHORT)IoMessage->u.GetString.LengthOfStringRead); } void DbgKdTransport::HandlePrintTrace(IN ULONG Processor, IN PUCHAR Data, IN USHORT DataLength, IN ULONG Mask) { // This routine can be called during a wait when the // engine lock isn't held and can also be called when // the lock is held. RESUME handles both of these // cases so that the lock is reacquired or reentered. RESUME_ENGINE(); DebugClient* Client; // Find a client with output callbacks to use for output. for (Client = g_Clients; Client != NULL; Client = Client->m_Next) { if (Client->m_OutputCb != NULL) { break; } } if (Client == NULL) { // No clients have output callbacks so nobody // cares about output and we can just quit. goto Exit; } // Prefix the entire output block with the processor // number as we can't (and don't want to) get involved // in the individual messages. if (m_Target->m_NumProcessors > 1 && Processor != g_LastProcessorToPrint) { g_LastProcessorToPrint = Processor; MaskOut(Mask, "%d", Processor); } if (g_WmiFormatTraceData == NULL) { EXTDLL* WmiExt; // Prevent notifications from this low level call. g_EngNotify++; WmiExt = AddExtensionDll("wmitrace", FALSE, m_Target, NULL); if (WmiExt) { LoadExtensionDll(m_Target, WmiExt); } g_EngNotify--; } if (g_WmiFormatTraceData == NULL) { ErrOut("Missing or incorrect wmitrace.dll - " "0x%X byte trace data buffer ignored\n", DataLength); } else { g_WmiFormatTraceData((PDEBUG_CONTROL)(IDebugControlN*)Client, Mask, DataLength, Data); } Exit: SUSPEND_ENGINE(); } KD_FILE_ASSOC* DbgKdTransport::FindKdFileAssoc(PWSTR From) { PLIST_ENTRY Entry; KD_FILE_ASSOC* Assoc; for (Entry = m_KdFileAssoc.Flink; Entry != &m_KdFileAssoc; Entry = Entry->Flink) { Assoc = CONTAINING_RECORD(Entry, KD_FILE_ASSOC, List); if (!_wcsicmp(From, Assoc->From)) { return Assoc; } } return NULL; } void DbgKdTransport::ClearKdFileAssoc(void) { while (!IsListEmpty(&m_KdFileAssoc)) { KD_FILE_ASSOC* Assoc; Assoc = CONTAINING_RECORD(m_KdFileAssoc.Flink, KD_FILE_ASSOC, List); RemoveEntryList(&Assoc->List); free(Assoc); } m_KdFileAssocSource[0] = 0; } HRESULT DbgKdTransport::LoadKdFileAssoc(PSTR FileName) { HRESULT Status; FILE* File; char Op[32], From[MAX_PATH], To[MAX_PATH]; File = fopen(FileName, "r"); if (File == NULL) { return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } ClearKdFileAssoc(); Status = S_OK; for (;;) { ULONG Len; if (fgets(Op, sizeof(Op), File) == NULL) { break; } // Remove newline if present. Len = strlen(Op); if (Len > 0 && Op[Len - 1] == '\n') { Op[Len - 1] = 0; } // Skip blank lines between sections and // allow comments starting with '#'. if (Op[0] == '#' || !Op[0]) { continue; } if (_stricmp(Op, "map") != 0) { Status = E_INVALIDARG; break; } if (fgets(From, sizeof(From), File) == NULL || fgets(To, sizeof(To), File) == NULL) { Status = E_INVALIDARG; break; } // Remove newlines if present. Len = strlen(From); if (Len > 0 && From[Len - 1] == '\n') { From[Len - 1] = 0; } Len = strlen(To); if (Len > 0 && To[Len - 1] == '\n') { To[Len - 1] = 0; } KD_FILE_ASSOC* Assoc; Assoc = (KD_FILE_ASSOC*)malloc(sizeof(KD_FILE_ASSOC) + (strlen(From) + 1) * sizeof(WCHAR) + strlen(To) + 1); if (Assoc == NULL) { Status = E_OUTOFMEMORY; break; } Assoc->From = (PWSTR)(Assoc + 1); if (MultiByteToWideChar(CP_ACP, 0, From, -1, Assoc->From, sizeof(From) / sizeof(WCHAR)) == 0) { Status = WIN32_LAST_STATUS(); break; } Assoc->To = (PSTR)(Assoc->From + strlen(From) + 1); strcpy(Assoc->To, To); InsertHeadList(&m_KdFileAssoc, &Assoc->List); } fclose(File); if (Status == S_OK) { CopyString(m_KdFileAssocSource, FileName, DIMA(m_KdFileAssocSource)); } return Status; } void DbgKdTransport::InitKdFileAssoc(void) { PSTR Env; InitializeListHead(&m_KdFileAssoc); m_KdFileAssocSource[0] = 0; Env = getenv("_NT_KD_FILES"); if (Env != NULL) { LoadKdFileAssoc(Env); } } void DbgKdTransport::ParseKdFileAssoc(void) { if (PeekChar() == ';' || *g_CurCmd == 0) { if (m_KdFileAssocSource[0]) { dprintf("KD file assocations loaded from '%s'\n", m_KdFileAssocSource); } else { dprintf("No KD file associations set\n"); } return; } while (PeekChar() == '-' || *g_CurCmd == '/') { g_CurCmd++; switch(*g_CurCmd++) { case 'c': ClearKdFileAssoc(); dprintf("KD file associations cleared\n"); return; default: ErrOut("Unknown option '%c'\n", *(g_CurCmd - 1)); break; } } PSTR FileName; CHAR Save; FileName = StringValue(STRV_TRIM_TRAILING_SPACE, &Save); if (LoadKdFileAssoc(FileName) == S_OK) { dprintf("KD file assocations loaded from '%s'\n", FileName); } else { dprintf("Unable to load KD file associations from '%s'\n", FileName); } *g_CurCmd = Save; } NTSTATUS DbgKdTransport::CreateKdFile(PWSTR FileName, ULONG DesiredAccess, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, KD_FILE** FileEntry, PULONG64 Length) { ULONG Access, Create; KD_FILE* File; KD_FILE_ASSOC* Assoc; Assoc = FindKdFileAssoc(FileName); if (Assoc == NULL) { return STATUS_NO_SUCH_FILE; } File = new KD_FILE; if (File == NULL) { return STATUS_NO_MEMORY; } Access = 0; if (DesiredAccess & FILE_READ_DATA) { Access |= GENERIC_READ; } if (DesiredAccess & FILE_WRITE_DATA) { Access |= GENERIC_WRITE; } switch(CreateDisposition) { case FILE_OPEN: Create = OPEN_EXISTING; break; case FILE_CREATE: Create = CREATE_NEW; break; case FILE_OPEN_IF: Create = OPEN_ALWAYS; break; case FILE_OVERWRITE_IF: Create = CREATE_ALWAYS; break; default: delete File; return STATUS_INVALID_PARAMETER; } // No interesting CreateOptions at this point. File->Handle = CreateFile(Assoc->To, Access, ShareAccess, NULL, Create, FileAttributes, NULL); if (File->Handle == NULL || File->Handle == INVALID_HANDLE_VALUE) { delete File; switch(GetLastError()) { case ERROR_FILE_NOT_FOUND: return STATUS_NO_SUCH_FILE; case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED; default: return STATUS_UNSUCCESSFUL; } } ULONG SizeLow; LONG SizeHigh = 0; SizeLow = SetFilePointer(File->Handle, 0, &SizeHigh, FILE_END); if (SizeLow == INVALID_SET_FILE_POINTER && GetLastError()) { CloseHandle(File->Handle); delete File; return STATUS_UNSUCCESSFUL; } *Length = ((ULONG64)SizeHigh << 32) | SizeLow; dprintf("KD: Accessing '%s' (%ws)\n ", Assoc->To, FileName); if (*Length > 0) { dprintf("File size %dK", KBYTES(*Length)); } // Progress dots will be printed for each read/write. File->Signature = KD_FILE_SIGNATURE; InsertHeadList(&m_KdFiles, &File->List); *FileEntry = File; return STATUS_SUCCESS; } void DbgKdTransport::CloseKdFile(KD_FILE* File) { RemoveEntryList(&File->List); CloseHandle(File->Handle); File->Signature = 0; delete File; } KD_FILE* DbgKdTransport::TranslateKdFileHandle(ULONG64 Handle) { KD_FILE* File = (KD_FILE*)(ULONG_PTR)Handle; if (IsBadWritePtr(File, sizeof(*File)) || File->Signature != KD_FILE_SIGNATURE) { return NULL; } return File; } //---------------------------------------------------------------------------- // // DbgKdComTransport. // //---------------------------------------------------------------------------- // Environment variable names. #define COM_PORT_NAME "_NT_DEBUG_PORT" #define COM_PORT_BAUD "_NT_DEBUG_BAUD_RATE" // Parameter string names. #define PARAM_COM_BAUD "Baud" #define PARAM_COM_IP_PORT "IpPort" #define PARAM_COM_MODEM "Modem" #define PARAM_COM_PIPE "Pipe" #define PARAM_COM_PORT "Port" #define PARAM_COM_RESETS "Resets" #define PARAM_COM_TIMEOUT "Timeout" DbgKdComTransport::DbgKdComTransport(ConnLiveKernelTargetInfo* Target) : DbgKdTransport(Target) { m_Index = DBGKD_TRANSPORT_COM; m_Name = g_DbgKdTransportNames[m_Index]; m_InvPacketRetryLimit = 0; m_AckWrites = TRUE; m_Handle = NULL; m_EventOverlapped.hEvent = NULL; } DbgKdComTransport::~DbgKdComTransport(void) { if (m_Handle != NULL) { CloseHandle(m_Handle); } if (m_EventOverlapped.hEvent != NULL) { CloseHandle(m_EventOverlapped.hEvent); } } ULONG DbgKdComTransport::GetNumberParameters(void) { return 5 + DbgKdTransport::GetNumberParameters(); } void DbgKdComTransport::GetParameter(ULONG Index, PSTR Name, ULONG NameSize, PSTR Value, ULONG ValueSize) { switch(Index) { case 0: CopyString(Name, PARAM_COM_PORT, NameSize); CopyString(Value, m_PortName, ValueSize); break; case 1: CopyString(Name, PARAM_COM_BAUD, NameSize); PrintString(Value, ValueSize, "%d", m_BaudRate); break; case 2: switch(m_PortType) { case COM_PORT_MODEM: CopyString(Name, PARAM_COM_MODEM, NameSize); break; case COM_PORT_PIPE: CopyString(Name, PARAM_COM_PIPE, NameSize); break; case COM_PORT_SOCKET: CopyString(Name, PARAM_COM_IP_PORT, NameSize); PrintString(Value, ValueSize, "%d", m_IpPort); break; } break; case 3: CopyString(Name, PARAM_COM_TIMEOUT, NameSize); PrintString(Value, ValueSize, "%d", m_Timeout); break; case 4: if (m_MaxSyncResets) { CopyString(Name, PARAM_COM_RESETS, NameSize); PrintString(Value, ValueSize, "%d", m_MaxSyncResets); } break; default: DbgKdTransport::GetParameter(Index - 4, Name, NameSize, Value, ValueSize); break; } } void DbgKdComTransport::ResetParameters(void) { PSTR Env; DbgKdTransport::ResetParameters(); if ((Env = getenv(COM_PORT_NAME)) == NULL) { Env = "com1"; } SetComPortName(Env, m_PortName, DIMA(m_PortName)); if ((Env = getenv(COM_PORT_BAUD)) != NULL) { m_BaudRate = atol(Env); } else { m_BaudRate = 19200; } m_PortType = COM_PORT_STANDARD; m_Timeout = 4000; m_CurTimeout = m_Timeout; m_MaxSyncResets = 0; m_IpPort = 0; } BOOL DbgKdComTransport::SetParameter(PCSTR Name, PCSTR Value) { if (!_strcmpi(Name, PARAM_COM_PORT)) { if (!Value) { ErrOut(PARAM_COM_PORT " requires a value\n"); return FALSE; } if (!SetComPortName(Value, m_PortName, DIMA(m_PortName))) { return FALSE; } } else if (!_strcmpi(Name, PARAM_COM_BAUD)) { if (!Value) { ErrOut(PARAM_COM_BAUD " requires a numeric value\n"); return FALSE; } m_BaudRate = atol(Value); } else if (!_strcmpi(Name, PARAM_COM_IP_PORT)) { if (!Value) { ErrOut(PARAM_COM_IP_PORT " requires a numeric value\n"); return FALSE; } m_PortType = COM_PORT_SOCKET; m_IpPort = atol(Value); } else if (!_strcmpi(Name, PARAM_COM_MODEM)) { m_PortType = COM_PORT_MODEM; } else if (!_strcmpi(Name, PARAM_COM_PIPE)) { m_PortType = COM_PORT_PIPE; m_MaxSyncResets = 2; } else if (!_strcmpi(Name, PARAM_COM_RESETS)) { if (!Value) { ErrOut(PARAM_COM_RESETS " requires a numeric value\n"); return FALSE; } m_MaxSyncResets = atol(Value); } else if (!_strcmpi(Name, PARAM_COM_TIMEOUT)) { if (!Value) { ErrOut(PARAM_COM_TIMEOUT " requires a numeric value\n"); return FALSE; } m_Timeout = atol(Value); m_CurTimeout = m_Timeout; } else { return DbgKdTransport::SetParameter(Name, Value); } return TRUE; } HRESULT DbgKdComTransport::Initialize(void) { HRESULT Status; if ((Status = DbgKdTransport::Initialize()) != S_OK) { return Status; } m_DirectPhysicalMemory = FALSE; COM_PORT_PARAMS ComParams; ZeroMemory(&ComParams, sizeof(ComParams)); ComParams.Type = m_PortType; ComParams.PortName = m_PortName; ComParams.BaudRate = m_BaudRate; ComParams.Timeout = m_Timeout; ComParams.IpPort = m_IpPort; if ((Status = OpenComPort(&ComParams, &m_Handle, &m_BaudRate)) != S_OK) { ErrOut("Failed to open %s\n", m_PortName); return Status; } dprintf("Opened %s\n", m_PortName); m_ComEvent = 0; if (m_PortType == COM_PORT_MODEM) { DWORD Mask; // // Debugger is being run over a modem. Set event to watch // carrier detect. // GetCommMask (m_Handle, &Mask); // set DDCD event if (!SetCommMask (m_Handle, Mask | 0xA0)) { ErrOut("Failed to set event for %s.\n", m_PortName); return WIN32_LAST_STATUS(); } m_EventOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!m_EventOverlapped.hEvent) { ErrOut("Failed to create EventOverlapped\n"); return WIN32_LAST_STATUS(); } m_EventOverlapped.Offset = 0; m_EventOverlapped.OffsetHigh = 0; // Fake an event, so modem status will be checked m_ComEvent = 1; } return S_OK; } BOOL DbgKdComTransport::Read( IN PVOID Buffer, IN ULONG SizeOfBuffer, IN PULONG BytesRead ) { if (IS_DUMP_TARGET(m_Target)) { ErrOut( "Attempted to read KD transport while " "debugging a crash dump\n" ); DebugBreak(); } if (m_ComEvent) { CheckComStatus (); } // // In pipe mode it's possible that only part of the // desired data is available, so loop reading pieces // of data as long as there are successful reads. // *BytesRead = 0; while (SizeOfBuffer > 0) { ULONG _BytesRead=0; if (ComPortRead(m_Handle, m_PortType, m_CurTimeout, Buffer, SizeOfBuffer, &_BytesRead, &m_ReadOverlapped)) { if (m_OutputIo & DBGKD_OUTPUT_READS) { OutputIo("COM: Read %d bytes of %d\n", Buffer, SizeOfBuffer, _BytesRead); } Buffer = (PVOID)((PUCHAR)Buffer + _BytesRead); SizeOfBuffer -= _BytesRead; *BytesRead += _BytesRead; m_BytesRead += _BytesRead; // If this isn't a net connection stop reading if // we got less than the requested amount of data. if (!NET_COM_PORT(m_PortType) && _BytesRead < SizeOfBuffer) { break; } } else if (*BytesRead == 0) { // If a read failed with nothing read return an error. return FALSE; } else { // Read failed, so stop trying to read. break; } } return TRUE; } BOOL DbgKdComTransport::Write( IN PVOID Buffer, IN ULONG SizeOfBuffer, IN PULONG BytesWritten ) { if (IS_DUMP_TARGET(m_Target)) { ErrOut( "Attempted to write KD transport " "while debugging a crash dump\n" ); DebugBreak(); } if (m_ComEvent) { CheckComStatus (); } // // Break up large writes in smaller chunks // to try and avoid sending too much data // to the target all at once. Sleep a bit // between chunks to let the target retrieve // data. // BOOL Succ = TRUE; *BytesWritten = 0; while (SizeOfBuffer > 0) { ULONG Request, Done; // By default we want to encourage vendors // to create machines with robust serial // support so we don't actually limit // the write size. #if THROTTLE_WRITES Request = 96; #else Request = 0xffffffff; #endif if (SizeOfBuffer < Request) { Request = SizeOfBuffer; } if (!ComPortWrite(m_Handle, m_PortType, Buffer, Request, &Done, &m_WriteOverlapped)) { Succ = FALSE; break; } if (m_OutputIo & DBGKD_OUTPUT_WRITES) { OutputIo("COM: Wrote %d bytes of %d\n", Buffer, Request, Done); } *BytesWritten += Done; if (Done <= Request) { break; } Buffer = (PVOID)((PUCHAR)Buffer + Done); SizeOfBuffer -= Done; Sleep(10); } m_BytesWritten += *BytesWritten; return Succ; } void DbgKdComTransport::CycleSpeed(void) { if (NET_COM_PORT(m_PortType)) { ErrOut("Net COM port baud is ignored\n"); return; } if (SetComPortBaud(m_Handle, 0, &m_BaudRate) != S_OK) { ErrOut("New Baud rate Could not be set on Com %I64x - remains %d.\n", (ULONG64)m_Handle, m_BaudRate); } else { dprintf("Baud rate set to %d\n", m_BaudRate); } } VOID DbgKdComTransport::Synchronize(VOID) { USHORT Index; UCHAR DataByte, PreviousDataByte; USHORT PacketType = 0; ULONG TimeoutCount = 0; COMMTIMEOUTS CommTimeouts; COMMTIMEOUTS OldTimeouts; DWORD BytesRead; BOOL Succ; BOOL SendReset; ULONG DataLoops = 0; ULONG ResetsSent = 0; // // Get the old time out values and hold them. // We then set a new total timeout value of // a fraction of the base timeout. // #define TIMEOUT_ITERATIONS 6 m_CurTimeout = m_Timeout / 8; if (!NET_COM_PORT(m_PortType)) { GetCommTimeouts(m_Handle, &OldTimeouts); CommTimeouts = OldTimeouts; CommTimeouts.ReadIntervalTimeout = 0; CommTimeouts.ReadTotalTimeoutMultiplier = 0; CommTimeouts.ReadTotalTimeoutConstant = m_CurTimeout; SetCommTimeouts(m_Handle, &CommTimeouts); } FlushCallbacks(); // Emulators that fill pipes from emulated serial ports // can end up buffering a huge amount of data. Drain // everything off before we start resyncing. if (NET_COM_PORT(m_PortType)) { while (Read(&DataByte, 1, &BytesRead) && BytesRead == 1) { // Loop } } // Always send a reset the first time around. SendReset = TRUE; while (TRUE) { Timeout: // When sending data to a pipe assume that it's stored // rather than being discarded as would happen with an // overfull true serial port. Therefore, limit the // total number of reset packets sent as stuffing more // in the pipe will just cause a huge number of responses. if (SendReset && (!m_MaxSyncResets || ResetsSent < m_MaxSyncResets)) { WriteControlPacket(PACKET_TYPE_KD_RESET, 0L); ResetsSent++; } // // Read packet leader // BOOL First = TRUE; Index = 0; do { if (g_EngStatus & ENG_STATUS_EXIT_CURRENT_WAIT) { KdOut("Synchronize interrupted by exit request\n"); goto Exit; } // // if we don't get response from kernel in 3 seconds we // will resend the reset packet if user does not type ctrl_c. // Otherwise, we send breakin character and wait for data again. // Succ = Read(&DataByte, 1, &BytesRead); if ((!Succ) || (BytesRead != 1)) { // // Check user input for control_c. If user types control_c, // we will send a breakin packet to the target. Hopefully, // target will send us a StateChange packet and we'll // stop waiting. // if (m_BreakIn || m_SyncBreakIn) { m_SyncBreakIn = FALSE; WriteBreakInPacket(); TimeoutCount = 0; continue; } TimeoutCount++; // // if we have been waiting for 3 seconds, resend RESYNC packet // if (TimeoutCount < TIMEOUT_ITERATIONS) { continue; } TimeoutCount = 0; KdOut("SYNCTARGET: Timeout.\n"); FlushCallbacks(); SendReset = TRUE; goto Timeout; } #if DBG_SYNCH if (Succ && BytesRead == 1 && First) { dprintf("First byte %X\n", DataByte); First = FALSE; } #endif if (Succ && BytesRead == 1 && ( DataByte == PACKET_LEADER_BYTE || DataByte == CONTROL_PACKET_LEADER_BYTE) ) { if ( Index == 0 ) { PreviousDataByte = DataByte; Index++; } else if ( DataByte == PreviousDataByte ) { Index++; } else { PreviousDataByte = DataByte; Index = 1; } } else { Index = 0; if (Succ && BytesRead == 1) { // The target machine is alive and talking but // the received data is in the middle of // a packet. Break out of the header byte // loop and consume up to a trailer byte. break; } } } while ( Index < 4 ); if (Index == 4 && DataByte == CONTROL_PACKET_LEADER_BYTE) { // // Read 2 byte Packet type // Succ = Read((PUCHAR)&PacketType, sizeof(PacketType), &BytesRead); if (Succ && BytesRead == sizeof(PacketType) && PacketType == PACKET_TYPE_KD_RESET) { KdOut("SYNCTARGET: Received KD_RESET ACK packet.\n"); m_PacketExpected = INITIAL_PACKET_ID; m_NextPacketToSend = INITIAL_PACKET_ID; KdOut("SYNCTARGET: Target synchronized successfully...\n"); FlushCallbacks(); goto Exit; } } // // If we receive Data Packet leader, it means target has not // receive our reset packet. So we loop back and send it again. // N.B. We need to wait until target finishes sending the packet. // Otherwise, we may be sending the reset packet while the target // is sending the packet. This might cause target loss the reset // packet. // // Sometimes machines just send continuous streams of // garbage, which can cause an infinite loop here if // the garbage never contains a trailing byte. Break // this loop after a certain amount of garbage is received. // Index = 0; while (DataByte != PACKET_TRAILING_BYTE && Index < 2 * PACKET_MAX_SIZE) { Succ = Read(&DataByte, 1, &BytesRead); if (!Succ || BytesRead != 1) { DataByte = ~PACKET_TRAILING_BYTE; break; } Index++; } #if DBG_SYNCH dprintf(" ate %x bytes\n", Index); FlushCallbacks(); #endif if (DataByte == PACKET_TRAILING_BYTE) { // We've consumed up to a trailing byte but // there's no guarantee that the byte is not // part of the payload of a packet. However, // the target is still talking to us so // avoid sending a reset and provoking more // packets. There are cases, though, where // the target machine continuously sends data // and we end up not sending any more reset // packets. Send a reset packet every once // in a while to make sure we don't get stuck here. if (++DataLoops == 4) { DataLoops = 0; SendReset = TRUE; } else { SendReset = FALSE; } } else { // Target stopped talking before we got a // data byte, so attempt to reset. SendReset = TRUE; } } Exit: m_CurTimeout = m_Timeout; if (!NET_COM_PORT(m_PortType)) { SetCommTimeouts(m_Handle, &OldTimeouts); } } ULONG DbgKdComTransport::ReadPacketContents(IN USHORT PacketType) { DWORD BytesRead; BOOL Succ; UCHAR DataByte; ULONG Checksum; ULONG SyncBit; ULONG WaitStatus; // // First read a packet leader // WaitForPacketLeader: WaitStatus = ReadPacketLeader(PacketType, &s_PacketHeader.PacketLeader); if (WaitStatus != DBGKD_WAIT_PACKET) { return WaitStatus; } if (m_AllowInitialBreak && (g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK)) { KdOut("Attempting to get initial breakpoint.\n"); WriteBreakInPacket(); } // We've either sent the initial break or we don't want // one. Either way we don't need to send another one. m_AllowInitialBreak = FALSE; // // Read packetLeader ONLY read two Packet Leader bytes. This do loop // filters out the remaining leader byte. // do { Succ = Read(&DataByte, 1, &BytesRead); if ((Succ) && BytesRead == 1) { if (DataByte == PACKET_LEADER_BYTE || DataByte == CONTROL_PACKET_LEADER_BYTE) { continue; } else { *(PUCHAR)&s_PacketHeader.PacketType = DataByte; break; } } else { goto WaitForPacketLeader; } } while (TRUE); // // Now we have valid packet leader. Read rest of the packet type. // Succ = Read(((PUCHAR)&s_PacketHeader.PacketType) + 1, sizeof(s_PacketHeader.PacketType) - 1, &BytesRead); if ((!Succ) || BytesRead != sizeof(s_PacketHeader.PacketType) - 1) { // // If we cannot read the packet type and if the packet leader // indicates this is a data packet, we need to ask for resend. // Otherwise we simply ignore the incomplete packet. // if (s_PacketHeader.PacketLeader == PACKET_LEADER) { WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); KdOut("READ: Data packet header Type error (short read).\n"); } goto WaitForPacketLeader; } // // Check the Packet type. // if ((m_Target->m_KdMaxPacketType == 0 && s_PacketHeader.PacketType >= PACKET_TYPE_MAX) || (m_Target->m_KdMaxPacketType > 0 && s_PacketHeader.PacketType >= m_Target->m_KdMaxPacketType)) { KdOut("READ: Received INVALID packet type.\n"); if (s_PacketHeader.PacketLeader == PACKET_LEADER) { WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); } goto WaitForPacketLeader; } KdOut(" PacketType=%x, ", s_PacketHeader.PacketType); // // Read ByteCount // Succ = Read(&s_PacketHeader.ByteCount, sizeof(s_PacketHeader.ByteCount), &BytesRead); if ((!Succ) || BytesRead != sizeof(s_PacketHeader.ByteCount)) { // // If we cannot read the packet type and if the packet leader // indicates this is a data packet, we need to ask for resend. // Otherwise we simply ignore the incomplete packet. // if (s_PacketHeader.PacketLeader == PACKET_LEADER) { WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); KdOut("READ: Data packet header ByteCount error (short read).\n"); } goto WaitForPacketLeader; } // // Check ByteCount // if (s_PacketHeader.ByteCount > PACKET_MAX_SIZE) { if (s_PacketHeader.PacketLeader == PACKET_LEADER) { WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); KdOut("READ: Data packet header ByteCount error (short read).\n"); } goto WaitForPacketLeader; } KdOut("ByteCount=%x, ", s_PacketHeader.ByteCount); // // Read Packet Id // Succ = Read(&s_PacketHeader.PacketId, sizeof(s_PacketHeader.PacketId), &BytesRead); if ((!Succ) || BytesRead != sizeof(s_PacketHeader.PacketId)) { // // If we cannot read the packet Id and if the packet leader // indicates this is a data packet, we need to ask for resend. // Otherwise we simply ignore the incomplete packet. // if (s_PacketHeader.PacketLeader == PACKET_LEADER) { WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); KdOut("READ: Data packet header Id error (short read).\n"); } goto WaitForPacketLeader; } KdOut("PacketId=%x,\n", s_PacketHeader.PacketId); // // Don't read checksum here as in some cases // it isn't sent with control packets. // if (s_PacketHeader.PacketLeader == CONTROL_PACKET_LEADER ) { if (s_PacketHeader.PacketType == PACKET_TYPE_KD_ACKNOWLEDGE ) { // // If we received an expected ACK packet and we are not // waiting for any new packet, update outgoing packet id // and return. If we are NOT waiting for ACK packet // we will keep on waiting. If the ACK packet // is not for the packet we send, ignore it and keep on waiting. // if (s_PacketHeader.PacketId != m_NextPacketToSend) { KdOut("READ: Received unmatched packet id = %lx, Type = %x\n", s_PacketHeader.PacketId, s_PacketHeader.PacketType); goto WaitForPacketLeader; } else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { m_NextPacketToSend ^= 1; KdOut("READ: Received correct ACK packet.\n"); FlushCallbacks(); return DBGKD_WAIT_ACK; } else { goto WaitForPacketLeader; } } else if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESET) { // // if we received Reset packet, reset the packet control variables // and resend earlier packet. // m_NextPacketToSend = INITIAL_PACKET_ID; m_PacketExpected = INITIAL_PACKET_ID; WriteControlPacket(PACKET_TYPE_KD_RESET, 0L); KdOut("DbgKdpWaitForPacket(): Recieved KD_RESET packet, " "send KD_RESET ACK packet\n"); FlushCallbacks(); return DBGKD_WAIT_FAILED; } else if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESEND) { KdOut("READ: Received RESEND packet\n"); FlushCallbacks(); return DBGKD_WAIT_FAILED; } else { // // Invalid packet header, ignore it. // KdOut("READ: Received Control packet with UNKNOWN type\n"); goto WaitForPacketLeader; } } else { // // The packet header is for data packet (not control packet). // Read Checksum. // Succ = Read(&s_PacketHeader.Checksum, sizeof(s_PacketHeader.Checksum), &BytesRead); if ((!Succ) || BytesRead != sizeof(s_PacketHeader.Checksum)) { WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); KdOut("READ: Data packet header " "checksum error (short read).\n"); goto WaitForPacketLeader; } if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { // // If we are waiting for ACK packet ONLY // and we receive a data packet header, check if the packet id // is what we expected. If yes, assume the acknowledge is lost // (but sent) and process the packet. // if (s_PacketHeader.PacketId == m_PacketExpected) { m_NextPacketToSend ^= 1; KdOut("READ: Received VALID data packet " "while waiting for ACK.\n"); } else { KdOut("READ: Received Data packet with unmatched ID = %lx\n", s_PacketHeader.PacketId); WriteControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, s_PacketHeader.PacketId); goto WaitForPacketLeader; } } } // // We are waiting for data packet and we received the packet header // for data packet. Perform the following checkings to make sure // it is the packet we are waiting for. // if ((s_PacketHeader.PacketId & ~SYNC_PACKET_ID) != INITIAL_PACKET_ID && (s_PacketHeader.PacketId & ~SYNC_PACKET_ID) != (INITIAL_PACKET_ID ^ 1)) { KdOut("READ: Received INVALID packet Id.\n"); return DBGKD_WAIT_RESEND; } Succ = Read(s_Packet, s_PacketHeader.ByteCount, &BytesRead); if ( (!Succ) || BytesRead != s_PacketHeader.ByteCount ) { KdOut("READ: Data packet error (short read).\n"); return DBGKD_WAIT_RESEND; } // // Make sure the next byte is packet trailing byte // Succ = Read(&DataByte, sizeof(DataByte), &BytesRead); if ( (!Succ) || BytesRead != sizeof(DataByte) || DataByte != PACKET_TRAILING_BYTE ) { KdOut("READ: Packet trailing byte timeout.\n"); return DBGKD_WAIT_RESEND; } // // Make sure the checksum is valid. // Checksum = ComputeChecksum(s_Packet, s_PacketHeader.ByteCount); if (Checksum != s_PacketHeader.Checksum) { KdOut("READ: Checksum error.\n"); return DBGKD_WAIT_RESEND; } // // We have a valid data packet. If the packetid is bad, we just // ack the packet to the sender will step ahead. If packetid is bad // but SYNC_PACKET_ID bit is set, we sync up. If packetid is good, // or SYNC_PACKET_ID is set, we take the packet. // KdOut("READ: Received Type %x data packet with id = %lx successfully.\n\n", s_PacketHeader.PacketType, s_PacketHeader.PacketId); SyncBit = s_PacketHeader.PacketId & SYNC_PACKET_ID; s_PacketHeader.PacketId = s_PacketHeader.PacketId & ~SYNC_PACKET_ID; // // Ack the packet. SYNC_PACKET_ID bit will ALWAYS be OFF. // WriteControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, s_PacketHeader.PacketId); // // Check the incoming packet Id. // if ((s_PacketHeader.PacketId != m_PacketExpected) && (SyncBit != SYNC_PACKET_ID)) { KdOut("READ: Unexpected Packet Id (Acked).\n"); goto WaitForPacketLeader; } else { if (SyncBit == SYNC_PACKET_ID) { // // We know SyncBit is set, so reset Expected Ids // KdOut("READ: Got Sync Id, reset PacketId.\n"); m_PacketExpected = s_PacketHeader.PacketId; m_NextPacketToSend = INITIAL_PACKET_ID; } m_PacketExpected ^= 1; } return DBGKD_WAIT_PACKET; } ULONG DbgKdComTransport::WritePacketContents(IN KD_PACKET* Packet, IN PVOID PacketData, IN USHORT PacketDataLength, IN PVOID MorePacketData OPTIONAL, IN USHORT MorePacketDataLength OPTIONAL, IN BOOL NoAck) { BOOL Succ; ULONG BytesWritten; // Lock to ensure all parts of the data are // sequential in the stream. RESUME_ENGINE(); // // Write the packet header // Succ = Write(Packet, sizeof(*Packet), &BytesWritten); if ( (!Succ) || BytesWritten != sizeof(*Packet)) { // // An error occured writing the header, so write it again // KdOut("WRITE: Packet header error.\n"); SUSPEND_ENGINE(); return DBGKD_WRITE_RESEND; } // // Write the primary packet data // Succ = Write(PacketData, PacketDataLength, &BytesWritten); if ( (!Succ) || BytesWritten != PacketDataLength ) { // // An error occured writing the primary packet data, // so write it again // KdOut("WRITE: Message header error.\n"); SUSPEND_ENGINE(); return DBGKD_WRITE_RESEND; } // // If secondary packet data was specified (WriteMemory, SetContext...) // then write it as well. // if ( ARGUMENT_PRESENT(MorePacketData) ) { Succ = Write(MorePacketData, MorePacketDataLength, &BytesWritten); if ( (!Succ) || BytesWritten != MorePacketDataLength ) { // // An error occured writing the secondary packet data, // so write it again // KdOut("WRITE: Message data error.\n"); SUSPEND_ENGINE(); return DBGKD_WRITE_RESEND; } } // // Output a packet trailing byte // do { Succ = Write(&s_PacketTrailingByte[0], sizeof(s_PacketTrailingByte), &BytesWritten); } while ((!Succ) || (BytesWritten != sizeof(s_PacketTrailingByte))); SUSPEND_ENGINE(); if (!NoAck) { ULONG Received; // // Wait for ACK // Received = WaitForPacket(PACKET_TYPE_KD_ACKNOWLEDGE, NULL); if (Received != DBGKD_WAIT_ACK) { KdOut("WRITE: Wait for ACK failed. Resend Packet.\n"); return DBGKD_WRITE_RESEND; } } return DBGKD_WRITE_PACKET; } ULONG DbgKdComTransport::ReadPacketLeader( IN ULONG PacketType, OUT PULONG PacketLeader ) { DWORD BytesRead; BOOL Succ; USHORT Index; UCHAR DataByte, PreviousDataByte; Index = 0; do { if (m_BreakIn) { if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { WriteBreakInPacket(); return DBGKD_WAIT_RESYNC; } } if (m_Resync) { m_Resync = FALSE; KdOut(" Resync packet id ..."); Synchronize(); KdOut(" Done.\n"); FlushCallbacks(); return DBGKD_WAIT_RESYNC; } if (g_EngStatus & ENG_STATUS_EXIT_CURRENT_WAIT) { KdOut("Packet read interrupted by exit request\n"); return DBGKD_WAIT_INTERRUPTED; } FlushCallbacks(); Succ = Read(&DataByte, 1, &BytesRead); if (Succ && BytesRead == 1 && ( DataByte == PACKET_LEADER_BYTE || DataByte == CONTROL_PACKET_LEADER_BYTE)) { if ( Index == 0 ) { PreviousDataByte = DataByte; Index++; } else if ( DataByte == PreviousDataByte ) { Index++; } else { PreviousDataByte = DataByte; Index = 1; } } else { Index = 0; if (BytesRead == 0) { KdOut("READ: Timeout.\n"); FlushCallbacks(); if (m_AllowInitialBreak && (g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK)) { KdOut("Attempting to get initial breakpoint.\n"); WriteBreakInPacket(); } return DBGKD_WAIT_FAILED; } } } while ( Index < 2 ); if ( DataByte != CONTROL_PACKET_LEADER_BYTE ) { *PacketLeader = PACKET_LEADER; } else { *PacketLeader = CONTROL_PACKET_LEADER; } return DBGKD_WAIT_PACKET; } void DbgKdComTransport::CheckComStatus(void) /*++ Routine Description: Called when the com port status trigger signals a change. This function handles the change in status. Note: status is only monitored when being used over the modem. --*/ { DWORD CommStat; BOOL Succ; ULONG BytesRead; CHAR Buf[128]; DWORD CommErr; COMSTAT FullCommStat; ULONG Len; if (!m_ComEvent || NET_COM_PORT(m_PortType)) { // // Not triggered, just return // m_ComEvent = 0; return; } // This should succeed since we were just notified, // but check the return value to keep PREfix happy. if (!GetCommModemStatus(m_Handle, &CommStat)) { // Leave m_ComEvent set for another try. return; } m_ComEvent = 0; if (!(CommStat & 0x80)) { dprintf ("No carrier detect - in terminal mode\n"); // This routine can be called during a wait when the // engine lock isn't held and can also be called when // the lock is held. RESUME handles both of these // cases so that the lock is reacquired or reentered. RESUME_ENGINE(); // // Loop and read any com input // while (!(CommStat & 0x80)) { // // Get some input to send to the modem. // Len = GetInput("Term> ", Buf, DIMA(Buf), GETIN_DEFAULT); if (Len > 0) { Write(Buf, Len, &Len); Buf[0] = '\n'; Buf[1] = '\r'; Write(Buf, 2, &Len); } GetCommModemStatus(m_Handle, &CommStat); Succ = Read(Buf, sizeof(Buf), &BytesRead); if (Succ != TRUE || BytesRead == 0) { continue; } // // print the string. // dprintf("%s", Buf); FlushCallbacks(); // // if logging is on, log the output // if (g_LogFile != -1) { _write(g_LogFile, Buf, BytesRead); } } dprintf ("Carrier detect - returning to debugger\n"); FlushCallbacks(); ClearCommError(m_Handle, &CommErr, &FullCommStat); SUSPEND_ENGINE(); } else { CommErr = 0; ClearCommError(m_Handle, &CommErr, &FullCommStat); if (CommErr & CE_FRAME) { dprintf (" [FRAME ERR] "); } if (CommErr & CE_OVERRUN) { dprintf (" [OVERRUN ERR] "); } if (CommErr & CE_RXPARITY) { dprintf (" [PARITY ERR] "); } } // // Reset trigger // WaitCommEvent (m_Handle, &m_ComEvent, &m_EventOverlapped); } //---------------------------------------------------------------------------- // // DbgKd1394Transport. // //---------------------------------------------------------------------------- #define PARAM_1394_SYMLINK "Symlink" #define PARAM_1394_CHANNEL "Channel" #define ENV_1394_SYMLINK "_NT_DEBUG_1394_SYMLINK" #define ENV_1394_CHANNEL "_NT_DEBUG_1394_CHANNEL" DbgKd1394Transport::DbgKd1394Transport(ConnLiveKernelTargetInfo* Target) : DbgKdTransport(Target) { m_Index = DBGKD_TRANSPORT_1394; m_Name = g_DbgKdTransportNames[m_Index]; m_InvPacketRetryLimit = 3; m_AckWrites = FALSE; m_Handle = NULL; m_Handle2 = NULL; ZeroMemory(&m_ReadOverlapped2, sizeof(m_ReadOverlapped2)); } DbgKd1394Transport::~DbgKd1394Transport(void) { if (m_Handle != NULL) { CloseHandle(m_Handle); } if (m_Handle2 != NULL) { CloseHandle(m_Handle2); } if (m_ReadOverlapped2.hEvent != NULL) { CloseHandle(m_ReadOverlapped2.hEvent); } } ULONG DbgKd1394Transport::GetNumberParameters(void) { return 2 + DbgKdTransport::GetNumberParameters(); } void DbgKd1394Transport::GetParameter(ULONG Index, PSTR Name, ULONG NameSize, PSTR Value, ULONG ValueSize) { switch(Index) { case 0: if (m_SymlinkSpecified) { CopyString(Name, PARAM_1394_SYMLINK, NameSize); CopyString(Value, m_Symlink, ValueSize); } break; case 1: CopyString(Name, PARAM_1394_CHANNEL, NameSize); PrintString(Value, ValueSize, "%d", m_Channel); break; default: DbgKdTransport::GetParameter(Index - 2, Name, NameSize, Value, ValueSize); break; } } void DbgKd1394Transport::ResetParameters(void) { PSTR Env; DbgKdTransport::ResetParameters(); if ((Env = getenv(ENV_1394_SYMLINK)) == NULL) { m_SymlinkSpecified = FALSE; strcpy(m_Symlink, "Channel"); strcpy(m_Symlink2, "Instance"); } else { m_SymlinkSpecified = TRUE; CopyString(m_Symlink, Env, DIMA(m_Symlink)); } if ((Env = getenv(ENV_1394_CHANNEL)) == NULL) { m_Channel = 0; } else { m_Channel = atol(Env); } } BOOL DbgKd1394Transport::SetParameter(PCSTR Name, PCSTR Value) { if (!_strcmpi(Name, PARAM_1394_SYMLINK)) { if (!Value) { ErrOut(PARAM_1394_SYMLINK " requires a value\n"); return FALSE; } if (!CopyString(m_Symlink, Value, DIMA(m_Symlink))) { return FALSE; } m_SymlinkSpecified = TRUE; } else if (!_strcmpi(Name, PARAM_1394_CHANNEL)) { if (!Value) { ErrOut(PARAM_1394_CHANNEL " requires a numeric value\n"); return FALSE; } m_Channel = atol(Value); } else { return DbgKdTransport::SetParameter(Name, Value); } return TRUE; } HRESULT DbgKd1394Transport::Initialize(void) { CHAR InfFile[MAX_PATH]; char Name[64]; HRESULT Status; HANDLE InfHandle; dprintf("Using 1394 for debugging\n"); if ((Status = DbgKdTransport::Initialize()) != S_OK) { return Status; } m_ReadOverlapped2.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (m_ReadOverlapped2.hEvent == NULL) { return WIN32_LAST_STATUS(); } // // Debugging over 1394 requires drivers to be installed. // The the drivers registered so installation can succeed. // // Get the directory the debugger executable is in. // Remove the executable name and add the inf name. if (GetEngineDirectory(InfFile, DIMA(InfFile)) && CatString(InfFile, "\\1394\\1394dbg.inf", DIMA(InfFile))) { InfHandle = CreateFile(InfFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (InfHandle != INVALID_HANDLE_VALUE) { CloseHandle(InfHandle); typedef HDESK (__stdcall *PFN_SETUPCOPYOEMINFPROC)( PCSTR SourceInfFileName, PCSTR OEMSourceMediaLocation, DWORD OEMSourceMediaType, DWORD CopyStyle, PSTR DestinationInfFileName, DWORD DestinationInfFileNameSize, PDWORD RequiredSize, PSTR *DestinationInfFileNameComponent); HINSTANCE hmod = LoadLibrary("setupapi.dll"); PFN_SETUPCOPYOEMINFPROC pfnCopyOemInf = (PFN_SETUPCOPYOEMINFPROC) GetProcAddress(hmod, "SetupCopyOEMInfA"); CHAR DestinationInfFile[MAX_PATH]; ULONG RequiredSize; if (!pfnCopyOemInf) { dprintf("1394 debug drivers can not be installed on this platform\n"); return E_FAIL; } if (!(*pfnCopyOemInf)(InfFile, NULL, SPOST_PATH, SP_COPY_DELETESOURCE, DestinationInfFile, sizeof(DestinationInfFile), &RequiredSize, NULL)) { dprintf("Could not install 1394 device drivers - error %x\n", GetLastError()); return E_FAIL; } typedef BOOL (__stdcall *PFN_UPDATEDRIVER)( HWND HwndParent, LPCSTR HardwareId, LPCSTR FullInfPath, DWORD InstallFlags, PBOOL bRebootRequired); hmod = LoadLibrary("newdev.dll"); PFN_UPDATEDRIVER pfnUpdateDriver = (PFN_UPDATEDRIVER) GetProcAddress(hmod, "UpdateDriverForPlugAndPlayDevicesA"); if (pfnUpdateDriver) { if (!(*pfnUpdateDriver)(NULL, "V1394\\VIRTUAL_HOST_DEBUGGER", DestinationInfFile, INSTALLFLAG_FORCE, NULL) || !(*pfnUpdateDriver)(NULL, "V1394\\HOST_DEBUGGER", DestinationInfFile, INSTALLFLAG_FORCE, NULL)) { dprintf("Could not update 1394 device drivers - error %x\n", GetLastError()); } } } } m_DirectPhysicalMemory = TRUE; Status = Create1394Channel(m_Symlink, m_Channel, Name, DIMA(Name), &m_Handle); if (!m_SymlinkSpecified) { // The user didn't specify a symlink so we'll open // both and see which one responds. HRESULT Status2; Status2 = Create1394Channel(m_Symlink2, m_Channel, Name, DIMA(Name), &m_Handle2); if (Status2 == S_OK) { Status = S_OK; } } if (Status != S_OK) { ErrOut("Failed to open 1394 channel %d\n", m_Channel); ErrOut("If this is the first time KD was run, this is" " why this failed.\nVirtual 1394 " "Debugger Driver Installation will now be attempted\n"); return Status; } else { dprintf("Opened %s\n", Name); } // // put the virtual driver in the right operating mode.. // if (!SwitchVirtualDebuggerDriverMode (V1394DBG_API_CONFIGURATION_MODE_DEBUG)) { return E_FAIL; } return S_OK; } BOOL DbgKd1394Transport::Read( IN PVOID Buffer, IN ULONG SizeOfBuffer, IN PULONG BytesRead ) { BOOL Status; HANDLE Handles[2]; ULONG Count = 0; HANDLE FirstHandle = NULL; OVERLAPPED *FirstOverlapped = NULL; if (IS_DUMP_TARGET(m_Target)) { ErrOut("Attempted to read KD transport while " "debugging a crash dump\n"); DebugBreak(); } if (!SwitchVirtualDebuggerDriverMode (V1394DBG_API_CONFIGURATION_MODE_DEBUG)) { return FALSE; } // // We may have two handles open as we haven't decided // which symlink to use yet. Read on both and // pick whichever one answers first. // Status = ReadFile(m_Handle, Buffer, SizeOfBuffer, BytesRead, &m_ReadOverlapped); if (Status) { // Success on m_Handle, close m_Handle2. CloseSecond(FALSE); goto Exit; } else if (GetLastError() == ERROR_IO_PENDING) { Handles[Count++] = m_ReadOverlapped.hEvent; FirstHandle = m_Handle; FirstOverlapped = &m_ReadOverlapped; } if (m_Handle2) { Status = ReadFile(m_Handle2, Buffer, SizeOfBuffer, BytesRead, &m_ReadOverlapped2); if (Status) { // Success on m_Handle2, close m_Handle. CloseSecond(TRUE); goto Exit; } else if (GetLastError() == ERROR_IO_PENDING) { if (!Count) { FirstHandle = m_Handle2; FirstOverlapped = &m_ReadOverlapped2; } Handles[Count++] = m_ReadOverlapped2.hEvent; } } // // If both requests failed, Prevent looping on read errors from // burning 100% of the CPU. if (!Count) { Sleep(50); goto Exit; } // // We now have one or two pending I/Os, so wait to see // what completes. // ULONG Wait; Wait = WaitForMultipleObjects(Count, Handles, FALSE, INFINITE); switch(Wait) { case WAIT_OBJECT_0: if (Count == 2) { CancelIo(m_Handle2); } Status = GetOverlappedResult(FirstHandle, FirstOverlapped, BytesRead, FALSE); // Close the handle we are not using CloseSecond(FirstHandle == m_Handle2); break; case WAIT_OBJECT_0 + 1: CancelIo(m_Handle); Status = GetOverlappedResult(m_Handle2, &m_ReadOverlapped2, BytesRead, FALSE); CloseSecond(TRUE); break; default: CancelIo(FirstHandle); if (Count == 2) { CancelIo(m_Handle2); } Status = FALSE; break; } Exit: if (Status) { if (m_OutputIo & DBGKD_OUTPUT_READS) { OutputIo("1394: Read %d bytes of %d\n", Buffer, SizeOfBuffer, *BytesRead); } m_BytesRead += *BytesRead; } return Status; } BOOL DbgKd1394Transport::Write( IN PVOID Buffer, IN ULONG SizeOfBuffer, IN PULONG BytesWritten ) { BOOL Status; if (IS_DUMP_TARGET(m_Target)) { ErrOut("Attempted to write KD transport " "while debugging a crash dump\n"); DebugBreak(); } if (!SwitchVirtualDebuggerDriverMode (V1394DBG_API_CONFIGURATION_MODE_DEBUG)) { return FALSE; } // // We may have two handles open as we haven't decided // which symlink to use yet. Write to both. // Status = WriteFile(m_Handle, Buffer, SizeOfBuffer, BytesWritten, &m_WriteOverlapped); if (!Status) { if (GetLastError() == ERROR_IO_PENDING) { Status = GetOverlappedResult(m_Handle, &m_WriteOverlapped, BytesWritten, TRUE); if (!Status) { return Status; } } } if (m_Handle2) { Status = WriteFile(m_Handle2, Buffer, SizeOfBuffer, BytesWritten, &m_WriteOverlapped); if (!Status) { if (GetLastError() == ERROR_IO_PENDING) { Status = GetOverlappedResult(m_Handle2, &m_WriteOverlapped, BytesWritten, TRUE); if (!Status) { return Status; } } } } if (Status) { if (m_OutputIo & DBGKD_OUTPUT_WRITES) { OutputIo("1394: Wrote %d bytes of %d\n", Buffer, SizeOfBuffer, *BytesWritten); } m_BytesWritten += *BytesWritten; } return Status; } HRESULT DbgKd1394Transport::ReadTargetPhysicalMemory( IN ULONG64 MemoryOffset, IN PVOID Buffer, IN ULONG SizeofBuffer, IN PULONG BytesRead ) { DWORD dwRet, dwBytesRet; PV1394DBG_API_REQUEST pApiReq; if (IS_DUMP_TARGET(m_Target)) { ErrOut("Attempted to access KD transport while " "debugging a crash dump\n"); DebugBreak(); } if (m_Handle2) { // We should know what kind of communication is // occurring by now. ErrOut("Symlink must be established\n"); return E_UNEXPECTED; } // // first setup the read i/o parameters in the virtual driver // pApiReq = (PV1394DBG_API_REQUEST) LocalAlloc(LPTR, sizeof(V1394DBG_API_REQUEST)); if (pApiReq == NULL) { return E_OUTOFMEMORY; } // // if the virtual driver is not set in raw access mode, we need to // tell it to change modes.. // if (!SwitchVirtualDebuggerDriverMode (V1394DBG_API_CONFIGURATION_MODE_RAW_MEMORY_ACCESS)) { LocalFree(pApiReq); return E_UNEXPECTED; } pApiReq->RequestNumber = V1394DBG_API_SET_IO_PARAMETERS; pApiReq->Flags = V1394DBG_API_FLAG_READ_IO; pApiReq->u.SetIoParameters.fulFlags = 0; pApiReq->u.SetIoParameters.StartingMemoryOffset.QuadPart = MemoryOffset; dwRet = DeviceIoControl( m_Handle, IOCTL_V1394DBG_API_REQUEST, pApiReq, sizeof(V1394DBG_API_REQUEST), NULL, 0, &dwBytesRet, NULL ); if (!dwRet) { dwRet = GetLastError(); ErrOut("Failed to send SetIoParameters 1394 " "Virtual Driver Request, error %x\n",dwRet); LocalFree(pApiReq); return E_UNEXPECTED; } LocalFree(pApiReq); // // now do anormal read. The virtual driver will read SizeofBuffer bytes // starting at the remote PCs physical address we specified above // dwRet = ReadFile( m_Handle, Buffer, SizeofBuffer, BytesRead, &m_ReadOverlapped ); if (!dwRet) { if (GetLastError() == ERROR_IO_PENDING) { dwRet = GetOverlappedResult(m_Handle, &m_ReadOverlapped, BytesRead, TRUE); } } return (dwRet != 0) ? S_OK : E_UNEXPECTED; } BOOL DbgKd1394Transport::SwitchVirtualDebuggerDriverMode( IN ULONG DesiredOperationMode ) { DWORD dwRet, dwBytesRet; PV1394DBG_API_REQUEST pApiReq; // // If the virtual driver is not set in raw access mode, we need to // tell it to change modes.. // // We may have two handles open as we haven't decided // which symlink to use yet. Write to both. // if (m_OperationMode != DesiredOperationMode) { // // first setup the read i/o parameters in the virtual driver // pApiReq = (PV1394DBG_API_REQUEST) LocalAlloc(LPTR, sizeof(V1394DBG_API_REQUEST)); if (pApiReq == NULL) { return FALSE; } pApiReq->RequestNumber = V1394DBG_API_SET_CONFIGURATION; pApiReq->Flags = 0; pApiReq->u.SetConfiguration.OperationMode = DesiredOperationMode; dwRet = DeviceIoControl(m_Handle, IOCTL_V1394DBG_API_REQUEST, pApiReq, sizeof(V1394DBG_API_REQUEST), NULL, 0, &dwBytesRet, NULL); if (!dwRet) { dwRet = GetLastError(); ErrOut("Failed to send SetConfiguration 1394 " "Virtual Driver Request, error %x\n", dwRet); LocalFree(pApiReq); return FALSE; } if (m_Handle2) { dwRet = DeviceIoControl(m_Handle2, IOCTL_V1394DBG_API_REQUEST, pApiReq, sizeof(V1394DBG_API_REQUEST), NULL, 0, &dwBytesRet, NULL); if (!dwRet) { dwRet = GetLastError(); ErrOut("Failed to send SetConfiguration 1394 " "Virtual Driver Request, error %x\n", dwRet); LocalFree(pApiReq); return FALSE; } } m_OperationMode = DesiredOperationMode; LocalFree(pApiReq); } return TRUE; } VOID DbgKd1394Transport::Synchronize(VOID) { ULONG Index; ULONG BytesRead; BOOL Succ; // XXX drewb - Why is this code disabled? return; Index = 3; while (TRUE) { if (g_EngStatus & ENG_STATUS_EXIT_CURRENT_WAIT) { KdOut("Synchronize interrupted by exit request\n"); return; } WriteControlPacket(PACKET_TYPE_KD_RESET, 0L); FlushCallbacks(); Succ = Read(s_Packet, sizeof(s_Packet), &BytesRead); CopyMemory(&s_PacketHeader, &s_Packet[0], sizeof(KD_PACKET)); if (Succ && (BytesRead >= sizeof(s_PacketHeader))) { if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESET) { break; } } if (!Index--) { break; } } } ULONG DbgKd1394Transport::ReadPacketContents(IN USHORT PacketType) { DWORD BytesRead; BOOL Succ; ULONG Checksum; WaitForPacket1394: if (m_AllowInitialBreak && (g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK)) { KdOut("Attempting to get initial breakpoint.\n"); WriteBreakInPacket(); m_AllowInitialBreak = FALSE; } if (m_Resync) { m_Resync = FALSE; KdOut(" Resync packet id ..."); Synchronize(); KdOut(" Done.\n"); FlushCallbacks(); return DBGKD_WAIT_RESYNC; } if (m_BreakIn) { WriteBreakInPacket(); return DBGKD_WAIT_RESYNC; } if (g_EngStatus & ENG_STATUS_EXIT_CURRENT_WAIT) { KdOut("Packet read interrupted by exit request\n"); return DBGKD_WAIT_INTERRUPTED; } FlushCallbacks(); // // read the whole packet at once. // we try to read MAX_PACKET worth of data and then check how much // we really read. Also since the packet header (KD_PACKET) is part of what // we read, we later have to move the data packet back sizeof(KD_PACKET) // Succ = Read(s_Packet, sizeof(s_Packet), &BytesRead); CopyMemory(&s_PacketHeader, &s_Packet[0], sizeof(KD_PACKET)); if (!Succ || (BytesRead < sizeof(s_PacketHeader))) { if (!Succ) { KdOut("READ: Error %x.\n",GetLastError()); } else { KdOut("READ: Data ByteCount error (short read) %x, %x.\n", BytesRead, sizeof(s_PacketHeader)); } if (Succ && (BytesRead >= sizeof(s_PacketHeader)) ) { if (s_PacketHeader.PacketLeader == PACKET_LEADER) { WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); KdOut("READ: Data packet header " "ByteCount error (short read).\n"); } } goto WaitForPacket1394; } // // move data portion to start of packet. // MoveMemory(s_Packet, ((PUCHAR)s_Packet + sizeof(KD_PACKET)), BytesRead - sizeof(KD_PACKET)); // // Check the Packet type. // if ((m_Target->m_KdMaxPacketType == 0 && s_PacketHeader.PacketType >= PACKET_TYPE_MAX) || (m_Target->m_KdMaxPacketType > 0 && s_PacketHeader.PacketType >= m_Target->m_KdMaxPacketType)) { KdOut("READ: Received INVALID packet type.\n"); if (s_PacketHeader.PacketLeader == PACKET_LEADER) { return DBGKD_WAIT_RESEND; } return DBGKD_WAIT_FAILED; } KdOut(" PacketType=%x, ", s_PacketHeader.PacketType); // // Check ByteCount // if (s_PacketHeader.ByteCount > PACKET_MAX_SIZE ) { if (s_PacketHeader.PacketLeader == PACKET_LEADER) { KdOut("READ: Data packet header ByteCount error (short read).\n"); return DBGKD_WAIT_RESEND; } return DBGKD_WAIT_FAILED; } KdOut("ByteCount=%x, PacketId=%x,\n", s_PacketHeader.ByteCount, s_PacketHeader.PacketId); if (s_PacketHeader.ByteCount != (BytesRead - sizeof(s_PacketHeader))) { if (s_PacketHeader.PacketLeader == PACKET_LEADER) { KdOut("READ: Data packet header ByteCount error (short read).\n"); return DBGKD_WAIT_RESEND; } return DBGKD_WAIT_FAILED; } // // Make sure the checksum is valid. // Checksum = ComputeChecksum(s_Packet, s_PacketHeader.ByteCount); if (Checksum != s_PacketHeader.Checksum) { KdOut("READ: Checksum error.\n"); return DBGKD_WAIT_RESEND; } if (s_PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESET) { // // if we received Reset packet, reset the packet control variables // and resend earlier packet. // m_NextPacketToSend = INITIAL_PACKET_ID; m_PacketExpected = INITIAL_PACKET_ID; WriteControlPacket(PACKET_TYPE_KD_RESET, 0L); KdOut("DbgKdpWaitForPacket(): " "Recieved KD_RESET packet, send KD_RESET ACK packet\n"); FlushCallbacks(); return DBGKD_WAIT_FAILED; } else if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESEND) { KdOut("READ: Received RESEND packet\n"); FlushCallbacks(); return DBGKD_WAIT_FAILED; } else { // // Invalid packet header, ignore it. // KdOut("READ: Received Control packet with UNKNOWN type\n"); FlushCallbacks(); return DBGKD_WAIT_FAILED; } } // // we are waiting for data packet and we received the packet header // for data packet. Perform the following checkings to make sure // it is the packet we are waiting for. // KdOut("READ: Received Type %x data packet with id = %lx successfully.\n\n", s_PacketHeader.PacketType, s_PacketHeader.PacketId); return DBGKD_WAIT_PACKET; } ULONG DbgKd1394Transport::WritePacketContents(IN KD_PACKET* Packet, IN PVOID PacketData, IN USHORT PacketDataLength, IN PVOID MorePacketData OPTIONAL, IN USHORT MorePacketDataLength OPTIONAL, IN BOOL NoAck) { BOOL Succ; ULONG BytesWritten; PUCHAR Tx; // Lock to ensure only one thread is using // the transmit buffer. RESUME_ENGINE(); // // On 1394 we double buffer all packet segments into one contigious // buffer and write it all at once // Tx = m_TxPacket; memcpy(Tx, Packet, sizeof(*Packet)); Tx += sizeof(*Packet); memcpy(Tx, PacketData, PacketDataLength); Tx += PacketDataLength; if ( ARGUMENT_PRESENT(MorePacketData) ) { memcpy(Tx, MorePacketData, MorePacketDataLength); Tx += MorePacketDataLength; } // // The 1394 Debug protocol does not use trailer bytes // // // Write the whole packet out to the bus // do { Succ = Write(&m_TxPacket[0], (ULONG)(Tx - m_TxPacket), &BytesWritten); } while ((!Succ) || (BytesWritten != (ULONG)(Tx - m_TxPacket))); SUSPEND_ENGINE(); return DBGKD_WRITE_PACKET; } void DbgKd1394Transport::CloseSecond(BOOL MakeFirst) { if (!m_Handle2) { // No secondary open. return; } if (MakeFirst) { CloseHandle(m_Handle); m_Handle = m_Handle2; m_Handle2 = NULL; strcpy(m_Symlink, m_Symlink2); } else { CloseHandle(m_Handle2); m_Handle2 = NULL; } }