3261 lines
79 KiB
C++
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();
|
|
}
|