You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3807 lines
100 KiB
3807 lines
100 KiB
//----------------------------------------------------------------------------
|
|
//
|
|
// KD hard-line communication support.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1999-2002.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
|
|
#include <portio.h>
|
|
|
|
#include <setupapi.h>
|
|
#include <newdev.h>
|
|
|
|
#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;
|
|
}
|
|
}
|