windows-nt/Source/XPSP1/NT/sdktools/debuggers/ntsd64/dbgkdtrans.cpp
2020-09-26 16:20:57 +08:00

3261 lines
79 KiB
C++

//----------------------------------------------------------------------------
//
// KD hard-line communication support.
//
// Copyright (C) Microsoft Corporation, 1999-2001.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
#include <portio.h>
#define THROTTLE_WRITES 0
#define DBG_KD_READ 0
#define DBG_KD_WRITE 0
#define DBG_SYNCH 0
#define KD_FILE_SIGNATURE 'lFdK'
struct KD_FILE
{
LIST_ENTRY List;
HANDLE Handle;
ULONG Signature;
};
struct KD_FILE_ASSOC
{
LIST_ENTRY List;
PWSTR From;
PSTR To;
};
LIST_ENTRY g_KdFiles;
char g_KdFileAssocSource[MAX_PATH];
LIST_ENTRY g_KdFileAssoc;
ULONG g_LastProcessorToPrint = (ULONG) -1;
CHAR g_PrintBuf[PACKET_MAX_SIZE];
PCSTR g_DbgKdTransportNames[] =
{
"com", "1394"
};
DbgKdTransport* g_DbgKdTransport;
DbgKdComTransport g_DbgKdComTransport;
DbgKd1394Transport g_DbgKd1394Transport;
// 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");
}
}
KD_FILE_ASSOC*
FindKdFileAssoc(PWSTR From)
{
PLIST_ENTRY Entry;
KD_FILE_ASSOC* Assoc;
for (Entry = g_KdFileAssoc.Flink;
Entry != &g_KdFileAssoc;
Entry = Entry->Flink)
{
Assoc = CONTAINING_RECORD(Entry, KD_FILE_ASSOC, List);
if (!_wcsicmp(From, Assoc->From))
{
return Assoc;
}
}
return NULL;
}
void
ClearKdFileAssoc(void)
{
while (!IsListEmpty(&g_KdFileAssoc))
{
KD_FILE_ASSOC* Assoc;
Assoc = CONTAINING_RECORD(g_KdFileAssoc.Flink, KD_FILE_ASSOC, List);
RemoveEntryList(&Assoc->List);
free(Assoc);
}
g_KdFileAssocSource[0] = 0;
}
HRESULT
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 (;;)
{
if (fgets(Op, sizeof(Op), File) == NULL)
{
break;
}
// Remove newline.
Op[strlen(Op) - 1] = 0;
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.
From[strlen(From) - 1] = 0;
To[strlen(To) - 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(&g_KdFileAssoc, &Assoc->List);
}
fclose(File);
if (Status == S_OK)
{
strncat(g_KdFileAssocSource, FileName,
sizeof(g_KdFileAssocSource) - 1);
}
return Status;
}
void
InitKdFileAssoc(void)
{
PSTR Env;
InitializeListHead(&g_KdFileAssoc);
Env = getenv("_NT_KD_FILES");
if (Env != NULL)
{
LoadKdFileAssoc(Env);
}
}
void
ParseKdFileAssoc(void)
{
if (PeekChar() == ';' || *g_CurCmd == 0)
{
if (g_KdFileAssocSource[0])
{
dprintf("KD file assocations loaded from '%s'\n",
g_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
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_GENERIC_READ)
{
Access |= GENERIC_READ;
}
if (DesiredAccess & FILE_GENERIC_WRITE)
{
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(&g_KdFiles, &File->List);
*FileEntry = File;
return STATUS_SUCCESS;
}
void
CloseKdFile(KD_FILE* File)
{
RemoveEntryList(&File->List);
CloseHandle(File->Handle);
File->Signature = 0;
delete File;
}
KD_FILE*
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;
}
//----------------------------------------------------------------------------
//
// DbgKdTransport.
//
//----------------------------------------------------------------------------
void
DbgKdTransport::Restart(void)
{
//
// Reinitialize per-connection values.
//
while (!IsListEmpty(&g_KdFiles))
{
CloseKdFile(CONTAINING_RECORD(g_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)];
g_DbgKdTransport->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, 0x%X\n", Status);
}
InitializeListHead(&g_KdFiles);
return Status;
}
void
DbgKdTransport::Uninitialize(void)
{
if (m_ReadOverlapped.hEvent != NULL)
{
CloseHandle(m_ReadOverlapped.hEvent);
m_ReadOverlapped.hEvent = NULL;
}
if (m_WriteOverlapped.hEvent != NULL)
{
CloseHandle(m_WriteOverlapped.hEvent);
m_WriteOverlapped.hEvent = NULL;
}
}
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:
DbgKdpPrint(Packet->Processor,
(PSTR)(Packet + 1),
(SHORT)Packet->u.PrintString.LengthOfString,
DEBUG_OUTPUT_DEBUGGEE);
break;
case DbgKdGetStringApi:
DbgKdpHandlePromptString(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:
DbgKdpPrintTrace(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:
DbgKdpAcquireHardwareBp(Packet);
break;
case DbgKdReleaseHardwareBp:
DbgKdpReleaseHardwareBp(Packet);
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;
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.
//
g_DbgKdTransport->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)
{
DbgKdApi64 = TRUE;
}
else if (s_PacketHeader.PacketType == PACKET_TYPE_KD_STATE_CHANGE32)
{
PacketType = PACKET_TYPE_KD_STATE_CHANGE32;
DbgKdApi64 = FALSE;
}
KdOut("READ: Packet type = %x, DbgKdApi64 = %x\n",
s_PacketHeader.PacketType, DbgKdApi64);
}
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 (!DbgKdApi64 && 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,
&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 rc;
KdOut("Send Break in ...\n");
FlushCallbacks();
do
{
rc = Write(&s_BreakinPacket[0], sizeof(s_BreakinPacket),
&BytesWritten);
} while ((!rc) || (BytesWritten != sizeof(s_BreakinPacket)));
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 rc;
KD_PACKET Packet;
DBG_ASSERT( (g_KdMaxPacketType == 0 && PacketType < PACKET_TYPE_MAX) ||
(g_KdMaxPacketType > 0 && PacketType < g_KdMaxPacketType) );
Packet.PacketLeader = CONTROL_PACKET_LEADER;
Packet.ByteCount = 0;
Packet.PacketType = PacketType;
if ( PacketId )
{
Packet.PacketId = PacketId;
}
else
{
Packet.PacketId = 0;
}
Packet.Checksum = 0;
do
{
//
// Write the control packet header
//
rc = Write(&Packet, sizeof(Packet), &BytesWritten);
} while ( (!rc) || BytesWritten != sizeof(Packet) );
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( (g_KdMaxPacketType == 0 && PacketType < PACKET_TYPE_MAX) ||
(g_KdMaxPacketType > 0 && PacketType < g_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 (!DbgKdApi64 && 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;
}
//----------------------------------------------------------------------------
//
// 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_PORT "Port"
#define PARAM_COM_BAUD "Baud"
#define PARAM_COM_MODEM "Modem"
#define PARAM_COM_TIMEOUT "Timeout"
DbgKdComTransport::DbgKdComTransport(void)
{
m_Index = DBGKD_TRANSPORT_COM;
m_Name = g_DbgKdTransportNames[m_Index];
m_InvPacketRetryLimit = 0;
m_AckWrites = TRUE;
}
ULONG
DbgKdComTransport::GetNumberParameters(void)
{
return 4;
}
void
DbgKdComTransport::GetParameter(ULONG Index, PSTR Name, PSTR Value)
{
switch(Index)
{
case 0:
strcpy(Name, PARAM_COM_PORT);
strcpy(Value, m_PortName);
break;
case 1:
strcpy(Name, PARAM_COM_BAUD);
sprintf(Value, "%d", m_BaudRate);
break;
case 2:
if (m_Modem)
{
strcpy(Name, PARAM_COM_MODEM);
}
break;
case 3:
strcpy(Name, PARAM_COM_TIMEOUT);
sprintf(Value, "%d", m_Timeout);
break;
}
}
void
DbgKdComTransport::ResetParameters(void)
{
PSTR Env;
if ((Env = getenv(COM_PORT_NAME)) == NULL)
{
Env = "com1";
}
SetComPortName(Env, m_PortName);
if ((Env = getenv(COM_PORT_BAUD)) != NULL)
{
m_BaudRate = atol(Env);
}
else
{
m_BaudRate = 19200;
}
m_Modem = FALSE;
m_Timeout = 4000;
}
BOOL
DbgKdComTransport::SetParameter(PCSTR Name, PCSTR Value)
{
if (!_strcmpi(Name, PARAM_COM_PORT))
{
SetComPortName(Value, m_PortName);
}
else if (!_strcmpi(Name, PARAM_COM_BAUD))
{
m_BaudRate = atol(Value);
}
else if (!_strcmpi(Name, PARAM_COM_MODEM))
{
m_Modem = TRUE;
}
else if (!_strcmpi(Name, PARAM_COM_TIMEOUT))
{
m_Timeout = atol(Value);
}
else
{
ErrOut("COM port parameters: %s is not a valid parameter\n", Name);
return FALSE;
}
return TRUE;
}
HRESULT
DbgKdComTransport::Initialize(void)
{
HRESULT Status;
if ((Status = DbgKdTransport::Initialize()) != S_OK)
{
return Status;
}
m_DirectPhysicalMemory = FALSE;
if ((Status = OpenComPort(m_PortName, m_BaudRate, m_Timeout,
&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_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;
}
void
DbgKdComTransport::Uninitialize(void)
{
if (m_Handle != NULL)
{
CloseHandle(m_Handle);
m_Handle = NULL;
}
if (m_EventOverlapped.hEvent != NULL)
{
CloseHandle(m_EventOverlapped.hEvent);
m_EventOverlapped.hEvent = NULL;
}
DbgKdTransport::Uninitialize();
}
BOOL
DbgKdComTransport::Read(
IN PVOID Buffer,
IN ULONG SizeOfBuffer,
IN PULONG BytesRead
)
{
if (IS_DUMP_TARGET())
{
ErrOut( "Attempted to read KD transport while "
"debugging a crash dump\n" );
DebugBreak();
}
if (m_ComEvent)
{
CheckComStatus ();
}
if (ComPortRead(m_Handle, Buffer, SizeOfBuffer, BytesRead,
&m_ReadOverlapped))
{
#if DBG_KD_READ
OutputIo("CR: Read %d bytes of %d\n",
Buffer, SizeOfBuffer, *BytesRead);
#endif
m_BytesRead += *BytesRead;
return TRUE;
}
else
{
return FALSE;
}
}
BOOL
DbgKdComTransport::Write(
IN PVOID Buffer,
IN ULONG SizeOfBuffer,
IN PULONG BytesWritten
)
{
if (IS_DUMP_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, Buffer, Request, &Done,
&m_WriteOverlapped))
{
Succ = FALSE;
break;
}
#if DBG_KD_WRITE
OutputIo("CW: Write %d bytes of %d\n",
Buffer, Request, Done);
#endif
*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 (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 rc;
//
// Get the old time out values and hold them.
// We then set a new total timeout value of
// a fraction of the base timeout.
//
GetCommTimeouts(g_DbgKdTransport->m_Handle, &OldTimeouts);
CommTimeouts = OldTimeouts;
CommTimeouts.ReadIntervalTimeout = 0;
CommTimeouts.ReadTotalTimeoutMultiplier = 0;
CommTimeouts.ReadTotalTimeoutConstant = m_Timeout / 8;
#define TIMEOUT_ITERATIONS 6
SetCommTimeouts(g_DbgKdTransport->m_Handle, &CommTimeouts);
FlushCallbacks();
while (TRUE)
{
Timeout:
WriteControlPacket(PACKET_TYPE_KD_RESET, 0L);
//
// 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;
}
//
// 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
//
//
// 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.
//
rc = g_DbgKdTransport->Read(&DataByte, 1, &BytesRead);
if ((!rc) || (BytesRead != 1))
{
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();
goto Timeout;
}
#if DBG_SYNCH
if (rc && BytesRead == 1 && First)
{
dprintf("First byte %X\n", DataByte);
First = FALSE;
}
#endif
if (rc && 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 (rc && 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
//
rc = g_DbgKdTransport->Read((PUCHAR)&PacketType,
sizeof(PacketType), &BytesRead);
if (rc && 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.
//
Index = 0;
while (DataByte != PACKET_TRAILING_BYTE)
{
rc = g_DbgKdTransport->Read(&DataByte, 1, &BytesRead);
if (!rc || BytesRead != 1)
{
break;
}
Index++;
}
#if DBG_SYNCH
dprintf(" ate %x bytes\n", Index);
FlushCallbacks();
#endif
}
Exit:
SetCommTimeouts(g_DbgKdTransport->m_Handle, &OldTimeouts);
}
ULONG
DbgKdComTransport::ReadPacketContents(IN USHORT PacketType)
{
DWORD BytesRead;
BOOL rc;
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
{
rc = Read(&DataByte, 1, &BytesRead);
if ((rc) && 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.
//
rc = Read(((PUCHAR)&s_PacketHeader.PacketType) + 1,
sizeof(s_PacketHeader.PacketType) - 1, &BytesRead);
if ((!rc) || 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 ((g_KdMaxPacketType == 0 &&
s_PacketHeader.PacketType >= PACKET_TYPE_MAX) ||
(g_KdMaxPacketType > 0 &&
s_PacketHeader.PacketType >= g_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
//
rc = Read(&s_PacketHeader.ByteCount, sizeof(s_PacketHeader.ByteCount),
&BytesRead);
if ((!rc) || 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
//
rc = Read(&s_PacketHeader.PacketId, sizeof(s_PacketHeader.PacketId),
&BytesRead);
if ((!rc) || 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.
//
rc = Read(&s_PacketHeader.Checksum, sizeof(s_PacketHeader.Checksum),
&BytesRead);
if ((!rc) || 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;
}
rc = Read(s_Packet, s_PacketHeader.ByteCount, &BytesRead);
if ( (!rc) || 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
//
rc = Read(&DataByte, sizeof(DataByte), &BytesRead);
if ( (!rc) || 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 rc;
ULONG BytesWritten;
// Lock to ensure all parts of the data are
// sequential in the stream.
RESUME_ENGINE();
//
// Write the packet header
//
rc = Write(Packet, sizeof(*Packet), &BytesWritten);
if ( (!rc) || 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
//
rc = Write(PacketData, PacketDataLength, &BytesWritten);
if ( (!rc) || 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) )
{
rc = Write(MorePacketData, MorePacketDataLength, &BytesWritten);
if ( (!rc) || 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
{
rc = Write(&s_PacketTrailingByte[0],
sizeof(s_PacketTrailingByte),
&BytesWritten);
}
while ((!rc) || (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 rc;
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_FAILED;
}
FlushCallbacks();
rc = Read(&DataByte, 1, &BytesRead);
if (rc && 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 status;
BOOL rc;
ULONG br, bw;
CHAR buf[128];
DWORD CommErr;
COMSTAT CommStat;
ULONG Len;
if (!m_ComEvent)
{
//
// Not triggered, just return
//
return;
}
// This should succeed since we were just notified,
// but check the return value to keep PREfix happy.
if (!GetCommModemStatus(m_Handle, &status))
{
// Leave m_ComEvent set for another try.
return;
}
m_ComEvent = 0;
if (!(status & 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 (!(status & 0x80))
{
//
// Get some input to send to the modem.
//
Len = GetInput("Term> ", buf, sizeof(buf));
if (Len > 0)
{
Write(buf, Len, &Len);
buf[0] = '\n';
buf[1] = '\r';
Write(buf, 2, &Len);
}
GetCommModemStatus (m_Handle, &status);
rc = Read(buf, sizeof buf, &br);
if (rc != TRUE || br == 0)
{
continue;
}
//
// print the string.
//
dprintf("%s", buf);
FlushCallbacks();
//
// if logging is on, log the output
//
if (g_LogFile != -1)
{
_write(g_LogFile, buf, br);
}
}
dprintf ("Carrier detect - returning to debugger\n");
FlushCallbacks();
ClearCommError (
m_Handle,
&CommErr,
&CommStat
);
SUSPEND_ENGINE();
}
else
{
CommErr = 0;
ClearCommError (
m_Handle,
&CommErr,
&CommStat
);
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_CHANNEL "Channel"
#define ENV_1394_CHANNEL "_NT_DEBUG_1394_CHANNEL"
DbgKd1394Transport::DbgKd1394Transport(void)
{
m_Index = DBGKD_TRANSPORT_1394;
m_Name = g_DbgKdTransportNames[m_Index];
m_InvPacketRetryLimit = 3;
m_AckWrites = FALSE;
}
ULONG
DbgKd1394Transport::GetNumberParameters(void)
{
return 1;
}
void
DbgKd1394Transport::GetParameter(ULONG Index, PSTR Name, PSTR Value)
{
switch(Index)
{
case 0:
strcpy(Name, PARAM_1394_CHANNEL);
sprintf(Value, "%d", m_Channel);
break;
}
}
void
DbgKd1394Transport::ResetParameters(void)
{
PSTR Env;
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_CHANNEL))
{
m_Channel = atol(Value);
}
else
{
ErrOut("1394 port parameters: %s is not a valid parameter\n", Name);
return FALSE;
}
return TRUE;
}
HRESULT
DbgKd1394Transport::Initialize(void)
{
char Name[64];
HRESULT Status;
dprintf("Using 1394 for debugging\n");
if ((Status = DbgKdTransport::Initialize()) != S_OK)
{
return Status;
}
m_DirectPhysicalMemory = TRUE;
Status = Create1394Channel(m_Channel, Name, &m_Handle);
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 FALSE;
}
return S_OK;
}
void
DbgKd1394Transport::Uninitialize(void)
{
if (m_Handle != NULL)
{
CloseHandle(m_Handle);
m_Handle = NULL;
}
DbgKdTransport::Uninitialize();
}
BOOL
DbgKd1394Transport::Read(
IN PVOID Buffer,
IN ULONG SizeOfBuffer,
IN PULONG BytesRead
)
{
BOOL rc;
DWORD TrashErr;
COMSTAT TrashStat;
if (IS_DUMP_TARGET())
{
ErrOut( "Attempted to read KD transport while "
"debugging a crash dump\n" );
DebugBreak();
}
if (!SwitchVirtualDebuggerDriverMode(V1394DBG_API_CONFIGURATION_MODE_DEBUG)) {
return FALSE;
}
rc = ReadFile(
m_Handle,
Buffer,
SizeOfBuffer,
BytesRead,
&m_ReadOverlapped
);
if (!rc)
{
if (GetLastError() == ERROR_IO_PENDING)
{
rc = GetOverlappedResult(m_Handle,
&m_ReadOverlapped,
BytesRead,
TRUE);
}
else
{
// Prevent looping on read errors from
// burning 100% of the CPU.
Sleep(50);
}
}
if (rc)
{
m_BytesRead += *BytesRead;
}
return rc;
}
BOOL
DbgKd1394Transport::Write(
IN PVOID Buffer,
IN ULONG SizeOfBuffer,
IN PULONG BytesWritten
)
{
BOOL rc;
DWORD TrashErr;
COMSTAT TrashStat;
if (IS_DUMP_TARGET())
{
ErrOut( "Attempted to write KD transport "
"while debugging a crash dump\n" );
DebugBreak();
}
if (!SwitchVirtualDebuggerDriverMode(V1394DBG_API_CONFIGURATION_MODE_DEBUG)) {
return FALSE;
}
rc = WriteFile(
m_Handle,
Buffer,
SizeOfBuffer,
BytesWritten,
&m_WriteOverlapped
);
if (!rc)
{
if (GetLastError() == ERROR_IO_PENDING)
{
rc = GetOverlappedResult(m_Handle,
&m_WriteOverlapped,
BytesWritten,
TRUE);
}
}
if (rc)
{
m_BytesWritten += *BytesWritten;
}
return rc;
}
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())
{
ErrOut( "Attempted to access KD transport while "
"debugging a crash dump\n" );
DebugBreak();
}
//
// 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..
//
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;
}
m_OperationMode = DesiredOperationMode;
LocalFree(pApiReq);
}
return TRUE;
}
VOID
DbgKd1394Transport::Synchronize(VOID)
{
ULONG Index;
ULONG BytesRead;
BOOL rc;
// 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();
rc = Read(s_Packet, sizeof(s_Packet), &BytesRead);
CopyMemory(&s_PacketHeader, &s_Packet[0], sizeof(KD_PACKET));
if (rc && (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 rc;
UCHAR DataByte;
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_FAILED;
}
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)
//
rc = Read(s_Packet, sizeof(s_Packet), &BytesRead);
CopyMemory(&s_PacketHeader, &s_Packet[0], sizeof(KD_PACKET));
if (!rc || (BytesRead < sizeof(s_PacketHeader)))
{
if (!rc)
{
KdOut("READ: Error %x.\n",GetLastError());
}
else
{
KdOut("READ: Data ByteCount error (short read) %x, %x.\n",
BytesRead, sizeof(s_PacketHeader));
}
if (rc && (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 ((g_KdMaxPacketType == 0 &&
s_PacketHeader.PacketType >= PACKET_TYPE_MAX) ||
(g_KdMaxPacketType > 0 &&
s_PacketHeader.PacketType >= g_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 rc;
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
{
rc = Write(&m_TxPacket[0], (ULONG)(Tx - m_TxPacket), &BytesWritten);
}
while ((!rc) || (BytesWritten != (ULONG)(Tx - m_TxPacket)));
SUSPEND_ENGINE();
return DBGKD_WRITE_PACKET;
}
//----------------------------------------------------------------------------
//
// Functions.
//
//----------------------------------------------------------------------------
#define BUS_TYPE "_NT_DEBUG_BUS"
#define DBG_BUS1394_NAME "1394"
HRESULT
DbgKdConnectAndInitialize(PCSTR Options)
{
DbgKdTransport* Trans = NULL;
ULONG Index;
// Try and find the transport by name.
Index = ParameterStringParser::
GetParser(Options, DBGKD_TRANSPORT_COUNT, g_DbgKdTransportNames);
if (Index < DBGKD_TRANSPORT_COUNT)
{
switch(Index)
{
case DBGKD_TRANSPORT_COM:
Trans = &g_DbgKdComTransport;
break;
case DBGKD_TRANSPORT_1394:
Trans = &g_DbgKd1394Transport;
break;
}
}
if (Trans == NULL)
{
PCHAR BusType;
// Couldn't identify the transport from options so check
// the environment.
// Default to com port.
Trans = &g_DbgKdComTransport;
if (BusType = getenv(BUS_TYPE))
{
if (strstr(BusType, DBG_BUS1394_NAME))
{
Trans = &g_DbgKd1394Transport;
}
}
}
HRESULT Status;
// Clear parameter state.
Trans->ResetParameters();
if (!Trans->ParseParameters(Options))
{
Status = E_INVALIDARG;
}
else
{
Status = Trans->Initialize();
if (Status != S_OK)
{
ErrOut("Kernel Debugger failed initialization, 0x%X\n", Status);
}
}
if (Status == S_OK)
{
g_DbgKdTransport = Trans;
Trans->Restart();
}
return Status;
}
VOID
DbgKdpPrint(
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 (g_TargetNumberProcessors > 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
DbgKdpHandlePromptString(
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);
DbgKdpPrint(IoMessage->Processor,
IoData,
(USHORT)IoMessage->u.GetString.LengthOfPromptString,
DEBUG_OUTPUT_DEBUGGEE_PROMPT
);
//
// read the prompt data
//
j = GetInput(NULL, IoData,
IoMessage->u.GetString.LengthOfStringRead);
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;
}
//
// Log the user's input
//
if (g_LogFile != -1)
{
_write(g_LogFile, IoData, j);
_write(g_LogFile, "\n", 1);
}
SUSPEND_ENGINE();
//
// Send data to the debugger-target
//
g_DbgKdTransport->WritePacket(IoMessage, sizeof(*IoMessage),
PACKET_TYPE_KD_DEBUG_IO,
IoData,
(USHORT)IoMessage->
u.GetString.LengthOfStringRead);
}
VOID
DbgKdpPrintTrace(
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 (g_TargetNumberProcessors > 1 && Processor != g_LastProcessorToPrint)
{
g_LastProcessorToPrint = Processor;
MaskOut(Mask, "%d", Processor);
}
if (g_WmiFormatTraceData == NULL)
{
AddExtensionDll("wmikd.dll", FALSE, NULL);
}
if (g_WmiFormatTraceData == NULL)
{
ErrOut("Missing or incorrect wmikd.dll - "
"0x%X byte trace data buffer ignored\n",
DataLength);
}
else
{
g_WmiFormatTraceData((PDEBUG_CONTROL)(IDebugControlN*)Client,
Mask, DataLength, Data);
}
Exit:
SUSPEND_ENGINE();
}