diff --git a/reactos/cmake/gcc.cmake b/reactos/cmake/gcc.cmake index 7cf984eb5d7..f27d80a0bb1 100644 --- a/reactos/cmake/gcc.cmake +++ b/reactos/cmake/gcc.cmake @@ -62,13 +62,13 @@ set(REACTOS_SOURCE_DIR_NATIVE ${REACTOS_SOURCE_DIR}) string(REPLACE "/" "\\" REACTOS_SOURCE_DIR_NATIVE ${REACTOS_SOURCE_DIR}) endif() -if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") +if((NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") AND (NOT SEPARATE_DBG)) add_compile_flags("-fdebug-prefix-map=\"${REACTOS_SOURCE_DIR_NATIVE}\"=ReactOS") endif() # Debugging if(SEPARATE_DBG) - add_compile_flags("-gdwarf-2 -g2") + add_compile_flags("-gdwarf-4 -fvar-tracking-assignments") else() add_compile_flags("-gdwarf-2 -gstrict-dwarf") if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") diff --git a/reactos/drivers/base/CMakeLists.txt b/reactos/drivers/base/CMakeLists.txt index 2a539a84aef..7d84024299a 100644 --- a/reactos/drivers/base/CMakeLists.txt +++ b/reactos/drivers/base/CMakeLists.txt @@ -4,9 +4,13 @@ add_subdirectory(bootvid) add_subdirectory(condrv) if(_WINKD_) -add_subdirectory(kdcom) + if (GDB) + add_subdirectory(kdgdb) + else() + add_subdirectory(kdcom) + endif() else() -add_subdirectory(kdrosdbg) + add_subdirectory(kdrosdbg) endif() add_subdirectory(nmidebug) diff --git a/reactos/drivers/base/kdgdb/CMakeLists.txt b/reactos/drivers/base/kdgdb/CMakeLists.txt new file mode 100644 index 00000000000..cf180c1a680 --- /dev/null +++ b/reactos/drivers/base/kdgdb/CMakeLists.txt @@ -0,0 +1,29 @@ + +spec2def(kdcom.dll kdgdb.spec ADD_IMPORTLIB) + +list(APPEND SOURCE + gdb_input.c + gdb_receive.c + gdb_send.c + kdcom.c + kdpacket.c + kdgdb.h) + +# TODO: AMD64, ARM... +if(ARCH STREQUAL "i386") + list(APPEND SOURCE i386_sup.c) +endif() + +add_library(kdcom SHARED + ${SOURCE} + kdgdb.rc + ${CMAKE_CURRENT_BINARY_DIR}/kdcom.def) + +set_entrypoint(kdcom 0) +set_subsystem(kdcom native) +set_image_base(kdcom 0x00010000) + +add_importlibs(kdcom ntoskrnl hal) +target_link_libraries(kdcom cportlib) +add_pch(kdcom kdgdb.h SOURCE) +add_cd_file(TARGET kdcom DESTINATION reactos/system32 NO_CAB FOR all) diff --git a/reactos/drivers/base/kdgdb/gdb_input.c b/reactos/drivers/base/kdgdb/gdb_input.c new file mode 100644 index 00000000000..2305055bd06 --- /dev/null +++ b/reactos/drivers/base/kdgdb/gdb_input.c @@ -0,0 +1,285 @@ +/* + * COPYRIGHT: GPL, see COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/base/kddll/gdb_input.c + * PURPOSE: Base functions for the kernel debugger. + */ + +#include "kdgdb.h" + +/* LOCALS *********************************************************************/ +static HANDLE gdb_run_thread; +static HANDLE gdb_dbg_process; +HANDLE gdb_dbg_thread; + +/* PRIVATE FUNCTIONS **********************************************************/ +static +HANDLE +hex_to_thread(char* buffer) +{ + ULONG_PTR ret = 0; + char hex; + while (*buffer) + { + hex = hex_value(*buffer++); + if (hex < 0) + return (HANDLE)ret; + ret <<= 4; + ret += hex; + } + return (HANDLE)ret; +} + +static +ULONG64 +hex_to_address(char* buffer) +{ + ULONG64 ret = 0; + char hex; + while (*buffer) + { + hex = hex_value(*buffer++); + if (hex < 0) + return ret; + ret <<= 4; + ret += hex; + } + return ret; +} + +/* H* packets */ +static +void +handle_gdb_set_thread(void) +{ + switch (gdb_input[1]) + { + case 'c': + if (strcmp(&gdb_input[2], "-1") == 0) + gdb_run_thread = (HANDLE)-1; + else + gdb_run_thread = hex_to_thread(&gdb_input[2]); + send_gdb_packet("OK"); + break; + case 'g': + if (strncmp(&gdb_input[2], "p-1", 3) == 0) + { + gdb_dbg_process = (HANDLE)-1; + gdb_dbg_thread = (HANDLE)-1; + } + else + { + char* ptr = strstr(gdb_input, ".") + 1; + gdb_dbg_process = hex_to_thread(&gdb_input[3]); + if (strncmp(ptr, "-1", 2) == 0) + gdb_dbg_thread = (HANDLE)-1; + else + gdb_dbg_thread = hex_to_thread(ptr); + } + send_gdb_packet("OK"); + break; + default: + KDDBGPRINT("KDGBD: Unknown 'H' command: %s\n", gdb_input); + send_gdb_packet(""); + } +} + +static +void +handle_gdb_thread_alive(void) +{ + char* ptr = strstr(gdb_input, ".") + 1; + CLIENT_ID ClientId; + PETHREAD Thread; + NTSTATUS Status; + + ClientId.UniqueProcess = hex_to_thread(&gdb_input[2]); + ClientId.UniqueThread = hex_to_thread(ptr); + + Status = PsLookupProcessThreadByCid(&ClientId, NULL, &Thread); + + if (!NT_SUCCESS(Status)) + { + /* Thread doesn't exist */ + send_gdb_packet("E03"); + return; + } + + /* It's OK */ + ObDereferenceObject(Thread); + send_gdb_packet("OK"); +} + +/* q* packets */ +static +void +handle_gdb_query(void) +{ + if (strncmp(gdb_input, "qSupported:", 11) == 0) + { + send_gdb_packet("PacketSize=4096;multiprocess+;"); + return; + } + + if (strncmp(gdb_input, "qAttached", 9) == 0) + { + /* Say yes: the remote server didn't create the process, ReactOS did! */ + send_gdb_packet("0"); + return; + } + + if (strncmp(gdb_input, "qRcmd,", 6) == 0) + { + send_gdb_packet("OK"); + return; + } + + if (strcmp(gdb_input, "qC") == 0) + { + char gdb_out[64]; + sprintf(gdb_out, "QC:p%p.%p;", + PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread), + PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread)); + send_gdb_packet(gdb_out); + return; + } + + if (strncmp(gdb_input, "qTStatus", 8) == 0) + { + /* We don't support tracepoints. */ + send_gdb_packet("T0"); + return; + } + + KDDBGPRINT("KDGDB: Unknown query: %s\n", gdb_input); + send_gdb_packet(""); +} + +#if 0 +static +KDSTATUS +handle_gdb_registers( + _Out_ DBGKD_MANIPULATE_STATE64* State, + _Out_ PSTRING MessageData, + _Out_ PULONG MessageLength) +{ + /* + if (gdb_dbg_thread) + KDDBGPRINT("Should get registers from other thread!\n"); + */ + + State->ApiNumber = DbgKdGetContextApi; + State->ReturnStatus = STATUS_SUCCESS; /* ? */ + State->Processor = CurrentStateChange.Processor; + State->ProcessorLevel = CurrentStateChange.ProcessorLevel; + if (MessageData) + MessageData->Length = 0; + *MessageLength = 0; + return KdPacketReceived; +} +#endif + +static +KDSTATUS +handle_gdb_read_mem( + _Out_ DBGKD_MANIPULATE_STATE64* State, + _Out_ PSTRING MessageData, + _Out_ PULONG MessageLength) +{ + State->ApiNumber = DbgKdReadVirtualMemoryApi; + State->ReturnStatus = STATUS_SUCCESS; /* ? */ + State->Processor = CurrentStateChange.Processor; + State->ProcessorLevel = CurrentStateChange.ProcessorLevel; + if (MessageData) + MessageData->Length = 0; + *MessageLength = 0; + + State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]); + State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1); + return KdPacketReceived; +} + +static +KDSTATUS +handle_gdb_v( + _Out_ DBGKD_MANIPULATE_STATE64* State, + _Out_ PSTRING MessageData, + _Out_ PULONG MessageLength, + _Inout_ PKD_CONTEXT KdContext) +{ + if (strncmp(gdb_input, "vCont", 5) == 0) + { + if (gdb_input[5] == '?') + { + KDSTATUS Status; + /* Report what we support */ + send_gdb_packet("vCont;c;C;s;S"); + Status = gdb_receive_packet(KdContext); + if (Status != KdPacketReceived) + return Status; + return gdb_interpret_input(State, MessageData, MessageLength, KdContext); + } + + if (strcmp(gdb_input, "vCont;c") == 0) + { + /* Let's go on */ + State->ApiNumber = DbgKdContinueApi; + State->ReturnStatus = STATUS_SUCCESS; /* ? */ + State->Processor = CurrentStateChange.Processor; + State->ProcessorLevel = CurrentStateChange.ProcessorLevel; + if (MessageData) + MessageData->Length = 0; + *MessageLength = 0; + State->u.Continue.ContinueStatus = STATUS_SUCCESS; + /* Tell GDB we are fine */ + send_gdb_packet("OK"); + return KdPacketReceived; + } + } + + return KdPacketReceived; +} + +/* GLOBAL FUNCTIONS ***********************************************************/ +KDSTATUS +gdb_interpret_input( + _Out_ DBGKD_MANIPULATE_STATE64* State, + _Out_ PSTRING MessageData, + _Out_ PULONG MessageLength, + _Inout_ PKD_CONTEXT KdContext) +{ + KDSTATUS Status; + switch (gdb_input[0]) + { + case '?': + /* Send the Status */ + gdb_send_exception(); + break; + case 'g': + gdb_send_registers(); + break; + case 'H': + handle_gdb_set_thread(); + break; + case 'm': + return handle_gdb_read_mem(State, MessageData, MessageLength); + case 'q': + handle_gdb_query(); + break; + case 'T': + handle_gdb_thread_alive(); + break; + case 'v': + return handle_gdb_v(State, MessageData, MessageLength, KdContext); + default: + /* We don't know how to handle this request. Maybe this is something for KD */ + State->ReturnStatus = STATUS_NOT_SUPPORTED; + return KdPacketReceived; + } + /* Get the answer from GDB */ + Status = gdb_receive_packet(KdContext); + if (Status != KdPacketReceived) + return Status; + /* Try interpreting this new packet */ + return gdb_interpret_input(State, MessageData, MessageLength, KdContext); +} diff --git a/reactos/drivers/base/kdgdb/gdb_receive.c b/reactos/drivers/base/kdgdb/gdb_receive.c new file mode 100644 index 00000000000..7494039ad95 --- /dev/null +++ b/reactos/drivers/base/kdgdb/gdb_receive.c @@ -0,0 +1,90 @@ +/* + * COPYRIGHT: GPL, see COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/base/kddll/gdb_receive.c + * PURPOSE: Base functions for the kernel debugger. + */ + +#include "kdgdb.h" + +/* GLOBALS ********************************************************************/ +CHAR gdb_input[0x1000]; + +/* GLOBAL FUNCTIONS ***********************************************************/ +char +hex_value(char ch) +{ + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + + return -1; +} + +KDSTATUS +NTAPI +gdb_receive_packet(_Inout_ PKD_CONTEXT KdContext) +{ + char* ByteBuffer = gdb_input; + UCHAR Byte; + KDSTATUS Status; + CHAR CheckSum = 0, ReceivedCheckSum; + + do + { + Status = KdpReceiveByte(&Byte); + if (Status != KdPacketReceived) + return Status; + if (Byte == 0x03) + { + KdContext->KdpControlCPending = TRUE; + return KdPacketNeedsResend; + } + } while (Byte != '$'); + + while (TRUE) + { + /* Try to get a byte from the port */ + Status = KdpReceiveByte(&Byte); + if (Status != KdPacketReceived) + return Status; + + if (Byte == '#') + { + *ByteBuffer = '\0'; + break; + } + + *ByteBuffer++ = Byte; + CheckSum += (CHAR)Byte; + } + + /* Get Check sum (two bytes) */ + Status = KdpReceiveByte(&Byte); + if (Status != KdPacketReceived) + goto end; + ReceivedCheckSum = hex_value(Byte) << 4;; + + Status = KdpReceiveByte(&Byte); + if (Status != KdPacketReceived) + goto end; + ReceivedCheckSum += hex_value(Byte); + +end: + if (ReceivedCheckSum != CheckSum) + { + /* Do not acknowledge to GDB */ + KdpSendByte('-'); + return KdPacketNeedsResend; + } + + /* Acknowledge */ + KdpSendByte('+'); + + return KdPacketReceived; +} diff --git a/reactos/drivers/base/kdgdb/gdb_send.c b/reactos/drivers/base/kdgdb/gdb_send.c new file mode 100644 index 00000000000..4e64495467e --- /dev/null +++ b/reactos/drivers/base/kdgdb/gdb_send.c @@ -0,0 +1,220 @@ +/* + * COPYRIGHT: GPL, see COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/base/kddll/gdb_send.c + * PURPOSE: Base functions for the kernel debugger. + */ + +#include "kdgdb.h" + +/* LOCALS *********************************************************************/ +const char hex_chars[] = "0123456789abcdef"; + +/* PRIVATE FUNCTIONS **********************************************************/ +static +char* +exception_code_to_gdb(NTSTATUS code, char* out) +{ + unsigned char SigVal; + + switch (code) + { + case STATUS_INTEGER_DIVIDE_BY_ZERO: + SigVal = 8; /* divide by zero */ + break; + case STATUS_SINGLE_STEP: + case STATUS_BREAKPOINT: + SigVal = 5; /* breakpoint */ + break; + case STATUS_INTEGER_OVERFLOW: + case STATUS_ARRAY_BOUNDS_EXCEEDED: + SigVal = 16; /* bound instruction */ + break; + case STATUS_ILLEGAL_INSTRUCTION: + SigVal = 4; /* Invalid opcode */ + break; + case STATUS_STACK_OVERFLOW: + case STATUS_DATATYPE_MISALIGNMENT: + case STATUS_ACCESS_VIOLATION: + SigVal = 11; /* access violation */ + break; + default: + SigVal = 7; /* "software generated" */ + } + *out++ = hex_chars[(SigVal >> 4) & 0xf]; + *out++ = hex_chars[SigVal & 0xf]; + return out; +} + +/* GLOBAL FUNCTIONS ***********************************************************/ +void +send_gdb_packet(_In_ CHAR* Buffer) +{ + UCHAR ack; + + do { + CHAR* ptr = Buffer; + CHAR check_sum = 0; + + KdpSendByte('$'); + + /* Calculate checksum */ + check_sum = 0; + while (*ptr) + { + check_sum += *ptr; + KdpSendByte(*ptr++); + } + + /* append it */ + KdpSendByte('#'); + KdpSendByte(hex_chars[(check_sum >> 4) & 0xf]); + KdpSendByte(hex_chars[check_sum & 0xf]); + + /* Wait for acknowledgement */ + if (KdpReceiveByte(&ack) != KdPacketReceived) + { + KD_DEBUGGER_NOT_PRESENT = TRUE; + break; + } + } while (ack != '+'); +} + +void +send_gdb_memory( + _In_ VOID* Buffer, + _In_ size_t Length) +{ + UCHAR ack; + + do { + CHAR* ptr = Buffer; + CHAR check_sum = 0; + size_t len = Length; + CHAR Byte; + + KdpSendByte('$'); + + /* Send the data */ + check_sum = 0; + while (len--) + { + Byte = hex_chars[(*ptr >> 4) & 0xf]; + KdpSendByte(Byte); + check_sum += Byte; + Byte = hex_chars[*ptr++ & 0xf]; + KdpSendByte(Byte); + check_sum += Byte; + } + + /* append check sum */ + KdpSendByte('#'); + KdpSendByte(hex_chars[(check_sum >> 4) & 0xf]); + KdpSendByte(hex_chars[check_sum & 0xf]); + + /* Wait for acknowledgement */ + if (KdpReceiveByte(&ack) != KdPacketReceived) + { + KD_DEBUGGER_NOT_PRESENT = TRUE; + break; + } + } while (ack != '+'); +} + +void +gdb_send_debug_io( + _In_ PSTRING String) +{ + UCHAR ack; + + do { + CHAR* ptr = String->Buffer; + CHAR check_sum; + USHORT Length = String->Length; + CHAR Byte; + + KdpSendByte('$'); + + KdpSendByte('O'); + + /* Send the data */ + check_sum = 'O'; + while (Length--) + { + Byte = hex_chars[(*ptr >> 4) & 0xf]; + KdpSendByte(Byte); + check_sum += Byte; + Byte = hex_chars[*ptr++ & 0xf]; + KdpSendByte(Byte); + check_sum += Byte; + } + + /* append check sum */ + KdpSendByte('#'); + KdpSendByte(hex_chars[(check_sum >> 4) & 0xf]); + KdpSendByte(hex_chars[check_sum & 0xf]); + + /* Wait for acknowledgement */ + if (KdpReceiveByte(&ack) != KdPacketReceived) + { + KD_DEBUGGER_NOT_PRESENT = TRUE; + break; + } + } while (ack != '+'); +} + +void +gdb_send_exception(void) +{ + char gdb_out[1024]; + char* ptr = gdb_out; + DBGKM_EXCEPTION64* Exception = NULL; + + if (CurrentStateChange.NewState == DbgKdExceptionStateChange) + Exception = &CurrentStateChange.u.Exception; + + /* Report to GDB */ + *ptr++ = 'T'; + if (Exception) + ptr = exception_code_to_gdb(Exception->ExceptionRecord.ExceptionCode, ptr); + else + ptr += sprintf(ptr, "05"); + ptr += sprintf(ptr, "thread:p%p.%p;", + PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread), + PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread)); + ptr += sprintf(ptr, "core:%x;", CurrentStateChange.Processor); + send_gdb_packet(gdb_out); +} + +#ifdef KDDEBUG +ULONG KdpDbgPrint(const char* Format, ...) +{ + va_list ap; + CHAR Buffer[512]; + struct _STRING Str; + int Length; + + va_start(ap, Format); + Length = _vsnprintf(Buffer, sizeof(Buffer), Format, ap); + va_end(ap); + + /* Check if we went past the buffer */ + if (Length == -1) + { + /* Terminate it if we went over-board */ + Buffer[sizeof(Buffer) - 1] = '\n'; + + /* Put maximum */ + Length = sizeof(Buffer); + } + + Str.Buffer = Buffer; + Str.Length = Length; + Str.MaximumLength = sizeof(Buffer); + + gdb_send_debug_io(&Str); + + return 0; +} +#endif + diff --git a/reactos/drivers/base/kdgdb/i386_sup.c b/reactos/drivers/base/kdgdb/i386_sup.c new file mode 100644 index 00000000000..86ddabc6ed7 --- /dev/null +++ b/reactos/drivers/base/kdgdb/i386_sup.c @@ -0,0 +1,99 @@ +/* + * COPYRIGHT: GPL, see COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/base/kddll/gdb_input.c + * PURPOSE: Base functions for the kernel debugger. + */ + +#include "kdgdb.h" + +enum reg_name +{ + EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, + EIP, + EFLAGS, + CS, SS, DS, ES, FS, GS, + ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7, + FCTRL, FSTAT, FTAG, FISEG, FIOFF, FOSEG, FOOFF, FOP, + XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, + MXCSR +}; + +static +void* +ctx_to_reg(CONTEXT* ctx, enum reg_name name, unsigned short* size) +{ + /* For general registers: 32bits */ + *size = 4; + switch (name) + { + case EAX: return &ctx->Eax; + case EBX: return &ctx->Ebx; + case ECX: return &ctx->Ecx; + case EDX: return &ctx->Edx; + case ESP: return &ctx->Esp; + case EBP: return &ctx->Ebp; + case ESI: return &ctx->Esi; + case EDI: return &ctx->Edi; + case EIP: return &ctx->Eip; + case EFLAGS: return &ctx->EFlags; + case CS: return &ctx->SegCs; + case DS: return &ctx->SegDs; + case ES: return &ctx->SegEs; + case FS: return &ctx->SegFs; + case GS: return &ctx->SegGs; + case SS: return &ctx->SegSs; + /* 80 bits */ + case ST0: + case ST1: + case ST2: + case ST3: + case ST4: + case ST5: + case ST6: + case ST7: + *size = 10; + return &ctx->FloatSave.RegisterArea[10 * (name - ST0)]; + /* X87 registers */ + case FCTRL: return &ctx->FloatSave.ControlWord; + case FSTAT: return &ctx->FloatSave.StatusWord; + case FTAG: return &ctx->FloatSave.TagWord; + case FISEG: return &ctx->FloatSave.DataSelector; + case FIOFF: return &ctx->FloatSave.DataOffset; + case FOSEG: return &ctx->FloatSave.ErrorSelector; + case FOOFF: return &ctx->FloatSave.ErrorOffset; + case FOP: return &ctx->FloatSave.Cr0NpxState; + /* SSE */ + case XMM0: + case XMM1: + case XMM2: + case XMM3: + case XMM4: + case XMM5: + case XMM6: + case XMM7: + *size = 16; + return &ctx->ExtendedRegisters[160 + (name - XMM0)*16]; + case MXCSR: return &ctx->ExtendedRegisters[24]; + } + return 0; +} + +void +gdb_send_registers(void) +{ + CONTEXT* ctx; + PKPRCB* ProcessorBlockLists; + ULONG32 Registers[16]; + unsigned i; + unsigned short size; + + ProcessorBlockLists = (PKPRCB*)KdDebuggerDataBlock->KiProcessorBlock.Pointer; + ctx = (CONTEXT*)((char*)ProcessorBlockLists[CurrentStateChange.Processor] + KdDebuggerDataBlock->OffsetPrcbProcStateContext); + + for(i=0; i < 16; i++) + { + Registers[i] = *(ULONG32*)ctx_to_reg(ctx, i, &size); + } + send_gdb_memory(Registers, sizeof(Registers)); +} diff --git a/reactos/drivers/base/kdgdb/kdcom.c b/reactos/drivers/base/kdgdb/kdcom.c new file mode 100644 index 00000000000..f07a4d80124 --- /dev/null +++ b/reactos/drivers/base/kdgdb/kdcom.c @@ -0,0 +1,269 @@ +/* + * COPYRIGHT: GPL, see COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/base/kdgdb/kdcom.c + * PURPOSE: COM port functions for the kernel debugger. + */ + +#include "kdgdb.h" + +#include +#include +#include +#include + +/* Serial debug connection */ +#define DEFAULT_DEBUG_PORT 2 /* COM2 */ +#define DEFAULT_DEBUG_COM1_IRQ 4 /* COM1 IRQ */ +#define DEFAULT_DEBUG_COM2_IRQ 3 /* COM2 IRQ */ +#define DEFAULT_DEBUG_BAUD_RATE 115200 /* 115200 Baud */ + +#define DEFAULT_BAUD_RATE 19200 + +#if defined(_M_IX86) || defined(_M_AMD64) +const ULONG BaseArray[] = {0, 0x3F8, 0x2F8, 0x3E8, 0x2E8}; +#elif defined(_M_PPC) +const ULONG BaseArray[] = {0, 0x800003F8}; +#elif defined(_M_MIPS) +const ULONG BaseArray[] = {0, 0x80006000, 0x80007000}; +#elif defined(_M_ARM) +const ULONG BaseArray[] = {0, 0xF1012000}; +#else +#error Unknown architecture +#endif + +/* GLOBALS ********************************************************************/ + +CPPORT KdDebugComPort; +ULONG KdDebugComPortIrq = 0; // Not used at the moment. + + +/* FUNCTIONS ******************************************************************/ + +NTSTATUS +NTAPI +KdD0Transition(VOID) +{ + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +KdD3Transition(VOID) +{ + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +KdSave(IN BOOLEAN SleepTransition) +{ + /* Nothing to do on COM ports */ + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +KdRestore(IN BOOLEAN SleepTransition) +{ + /* Nothing to do on COM ports */ + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +KdpPortInitialize(IN ULONG ComPortNumber, + IN ULONG ComPortBaudRate) +{ + NTSTATUS Status; + + Status = CpInitialize(&KdDebugComPort, + UlongToPtr(BaseArray[ComPortNumber]), + ComPortBaudRate); + if (!NT_SUCCESS(Status)) + { + return STATUS_INVALID_PARAMETER; + } + else + { + KdComPortInUse = KdDebugComPort.Address; + return STATUS_SUCCESS; + } +} + +/****************************************************************************** + * \name KdDebuggerInitialize0 + * \brief Phase 0 initialization. + * \param [opt] LoaderBlock Pointer to the Loader parameter block. Can be NULL. + * \return Status + */ +NTSTATUS +NTAPI +KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL) +{ + ULONG ComPortNumber = DEFAULT_DEBUG_PORT; + ULONG ComPortBaudRate = DEFAULT_DEBUG_BAUD_RATE; + + PCHAR CommandLine, PortString, BaudString, IrqString; + ULONG Value; + + /* Check if e have a LoaderBlock */ + if (LoaderBlock) + { + + /* Get the Command Line */ + CommandLine = LoaderBlock->LoadOptions; + + /* Upcase it */ + _strupr(CommandLine); + + /* Get the port and baud rate */ + PortString = strstr(CommandLine, "DEBUGPORT"); + BaudString = strstr(CommandLine, "BAUDRATE"); + IrqString = strstr(CommandLine, "IRQ"); + + /* Check if we got the /DEBUGPORT parameter */ + if (PortString) + { + /* Move past the actual string, to reach the port*/ + PortString += strlen("DEBUGPORT"); + + /* Now get past any spaces and skip the equal sign */ + while (*PortString == ' ') PortString++; + PortString++; + + /* Do we have a serial port? */ + if (strncmp(PortString, "COM", 3) != 0) + { + return STATUS_INVALID_PARAMETER; + } + + /* Check for a valid Serial Port */ + PortString += 3; + Value = atol(PortString); + if (Value >= sizeof(BaseArray) / sizeof(BaseArray[0])) + { + return STATUS_INVALID_PARAMETER; + } + + /* Set the port to use */ + ComPortNumber = Value; + } + + /* Check if we got a baud rate */ + if (BaudString) + { + /* Move past the actual string, to reach the rate */ + BaudString += strlen("BAUDRATE"); + + /* Now get past any spaces */ + while (*BaudString == ' ') BaudString++; + + /* And make sure we have a rate */ + if (*BaudString) + { + /* Read and set it */ + Value = atol(BaudString + 1); + if (Value) ComPortBaudRate = Value; + } + } + + /* Check Serial Port Settings [IRQ] */ + if (IrqString) + { + /* Move past the actual string, to reach the rate */ + IrqString += strlen("IRQ"); + + /* Now get past any spaces */ + while (*IrqString == ' ') IrqString++; + + /* And make sure we have an IRQ */ + if (*IrqString) + { + /* Read and set it */ + Value = atol(IrqString + 1); + if (Value) KdDebugComPortIrq = Value; + } + } + } + + /* Initialize the port */ + return KdpPortInitialize(ComPortNumber, ComPortBaudRate); +} + +/****************************************************************************** + * \name KdDebuggerInitialize1 + * \brief Phase 1 initialization. + * \param [opt] LoaderBlock Pointer to the Loader parameter block. Can be NULL. + * \return Status + */ +NTSTATUS +NTAPI +KdDebuggerInitialize1(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL) +{ + return STATUS_SUCCESS; +} + + +VOID +NTAPI +KdpSendByte(_In_ UCHAR Byte) +{ + /* Send the byte */ + CpPutByte(&KdDebugComPort, Byte); +} + +KDSTATUS +NTAPI +KdpPollByte(OUT PUCHAR OutByte) +{ + /* Poll the byte */ + if (CpGetByte(&KdDebugComPort, OutByte, FALSE, FALSE) == CP_GET_SUCCESS) + { + return KdPacketReceived; + } + else + { + return KdPacketTimedOut; + } +} + +KDSTATUS +NTAPI +KdpReceiveByte(_Out_ PUCHAR OutByte) +{ + /* Get the byte */ + if (CpGetByte(&KdDebugComPort, OutByte, TRUE, FALSE) == CP_GET_SUCCESS) + { + return KdPacketReceived; + } + else + { + return KdPacketTimedOut; + } +} + +KDSTATUS +NTAPI +KdpPollBreakIn(VOID) +{ + KDSTATUS KdStatus; + UCHAR Byte; + + KdStatus = KdpPollByte(&Byte); + if (KdStatus == KdPacketReceived) + { + if (Byte == 0x03) + { + return KdPacketReceived; + } + else if (Byte == '$') + { + /* GDB tried to send a new packet. N-ack it. */ + KdpSendByte('-'); + } + } + return KdPacketTimedOut; +} + +/* EOF */ diff --git a/reactos/drivers/base/kdgdb/kdgdb.h b/reactos/drivers/base/kdgdb/kdgdb.h new file mode 100644 index 00000000000..37b55106039 --- /dev/null +++ b/reactos/drivers/base/kdgdb/kdgdb.h @@ -0,0 +1,56 @@ +/* + * COPYRIGHT: GPL, see COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/base/kddll/kddll.h + * PURPOSE: Base definitions for the kernel debugger. + */ + +#ifndef _KDGDB_H_ +#define _KDGDB_H_ + +#define NOEXTAPI +#include +#include +#include +#include +#include +#include + +// #define KDDEBUG /* uncomment to enable debugging this dll */ + +#ifndef KDDEBUG +#define KDDBGPRINT(...) +#else +extern ULONG KdpDbgPrint(const char* Format, ...); +#define KDDBGPRINT KdpDbgPrint +#endif + +/* gdb_input.c */ +extern HANDLE gdb_dbg_thread; +KDSTATUS gdb_interpret_input(_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ PSTRING MessageData, _Out_ PULONG MessageLength, _Inout_ PKD_CONTEXT KdContext); + +/* gdb_receive.c */ +extern CHAR gdb_input[]; +KDSTATUS NTAPI gdb_receive_packet(_Inout_ PKD_CONTEXT KdContext); +char hex_value(char ch); + +/* gdb_send.c */ +void send_gdb_packet(_In_ CHAR* Buffer); +void send_gdb_memory(_In_ VOID* Buffer, size_t Length); +void gdb_send_debug_io(_In_ PSTRING String); +void gdb_send_exception(void); + +/* kdcom.c */ +KDSTATUS NTAPI KdpPollBreakIn(VOID); +VOID NTAPI KdpSendByte(_In_ UCHAR Byte); +KDSTATUS NTAPI KdpReceiveByte(_Out_ PUCHAR OutByte); + +/* kdpacket.c */ +extern DBGKD_ANY_WAIT_STATE_CHANGE CurrentStateChange; +extern DBGKD_GET_VERSION64 KdVersion; +extern KDDEBUGGER_DATA64* KdDebuggerDataBlock; + +/* arch_sup.c */ +void gdb_send_registers(void); + +#endif /* _KDGDB_H_ */ diff --git a/reactos/drivers/base/kdgdb/kdgdb.rc b/reactos/drivers/base/kdgdb/kdgdb.rc new file mode 100644 index 00000000000..d24a029c9ea --- /dev/null +++ b/reactos/drivers/base/kdgdb/kdgdb.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS GDB KDCOM wrapper DLL" +#define REACTOS_STR_INTERNAL_NAME "kdcom" +#define REACTOS_STR_ORIGINAL_FILENAME "kdcom.dll" +#include diff --git a/reactos/drivers/base/kdgdb/kdgdb.spec b/reactos/drivers/base/kdgdb/kdgdb.spec new file mode 100644 index 00000000000..4098dd6e2ad --- /dev/null +++ b/reactos/drivers/base/kdgdb/kdgdb.spec @@ -0,0 +1,8 @@ +@ stdcall KdD0Transition() +@ stdcall KdD3Transition() +@ stdcall KdDebuggerInitialize0(ptr) +@ stdcall KdDebuggerInitialize1(ptr) +@ stdcall KdReceivePacket(long ptr ptr ptr ptr) +@ stdcall KdRestore(long) +@ stdcall KdSave(long) +@ stdcall KdSendPacket(long ptr ptr ptr) diff --git a/reactos/drivers/base/kdgdb/kdpacket.c b/reactos/drivers/base/kdgdb/kdpacket.c new file mode 100644 index 00000000000..adcf15bb719 --- /dev/null +++ b/reactos/drivers/base/kdgdb/kdpacket.c @@ -0,0 +1,203 @@ +/* + * COPYRIGHT: GPL, see COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/base/kddll/kdpacket.c + * PURPOSE: Base functions for the kernel debugger. + */ + +#include "kdgdb.h" + +/* GLOBALS ********************************************************************/ + +DBGKD_ANY_WAIT_STATE_CHANGE CurrentStateChange; +DBGKD_GET_VERSION64 KdVersion; +KDDEBUGGER_DATA64* KdDebuggerDataBlock; + +/* LOCALS *********************************************************************/ +static BOOLEAN FakeNextManipulatePacket = FALSE; +static DBGKD_MANIPULATE_STATE64 FakeManipulateState = {0}; + +/* PRIVATE FUNCTIONS **********************************************************/ +static +void +send_kd_state_change(DBGKD_ANY_WAIT_STATE_CHANGE* StateChange) +{ + static BOOLEAN first = TRUE; + + /* Save current state for later GDB queries */ + CurrentStateChange = *StateChange; + + if (first) + { + /* + * This is the first packet we receive. + * We take this as an opportunity to connect with GDB and to + * get the KD version block + */ + FakeNextManipulatePacket = TRUE; + FakeManipulateState.ApiNumber = DbgKdGetVersionApi; + FakeManipulateState.Processor = StateChange->Processor; + FakeManipulateState.ProcessorLevel = StateChange->ProcessorLevel; + FakeManipulateState.ReturnStatus = STATUS_SUCCESS; + + first = FALSE; + return; + } + + switch (StateChange->NewState) + { + case DbgKdLoadSymbolsStateChange: + { + /* We don't care about symbols loading */ + FakeNextManipulatePacket = TRUE; + FakeManipulateState.ApiNumber = DbgKdContinueApi; + FakeManipulateState.Processor = StateChange->Processor; + FakeManipulateState.ProcessorLevel = StateChange->ProcessorLevel; + FakeManipulateState.ReturnStatus = STATUS_SUCCESS; + FakeManipulateState.u.Continue.ContinueStatus = STATUS_SUCCESS; + break; + } + case DbgKdExceptionStateChange: + gdb_send_exception(); + break; + default: + /* FIXME */ + while (1); + } +} + +static +void +send_kd_debug_io( + _In_ DBGKD_DEBUG_IO* DebugIO, + _In_ PSTRING String) +{ + switch (DebugIO->ApiNumber) + { + case DbgKdPrintStringApi: + gdb_send_debug_io(String); + break; + default: + /* FIXME */ + while (1); + } +} + +static +void +send_kd_state_manipulate( + _In_ DBGKD_MANIPULATE_STATE64* State, + _In_ PSTRING MessageData) +{ + switch (State->ApiNumber) + { +#if 0 + case DbgKdGetContextApi: + /* This is an answer to a 'g' GDB request */ + gdb_send_registers((CONTEXT*)MessageData->Buffer); + return; +#endif + case DbgKdReadVirtualMemoryApi: + /* Answer to 'm' GDB request */ + send_gdb_memory(MessageData->Buffer, State->u.ReadMemory.ActualBytesRead); + break; + case DbgKdGetVersionApi: + { + LIST_ENTRY* DebuggerDataList; + /* Simply get a copy */ + RtlCopyMemory(&KdVersion, &State->u.GetVersion64, sizeof(KdVersion)); + DebuggerDataList = (LIST_ENTRY*)(ULONG_PTR)KdVersion.DebuggerDataList; + KdDebuggerDataBlock = CONTAINING_RECORD(DebuggerDataList->Flink, KDDEBUGGER_DATA64, Header.List); + return; + } + default: + /* FIXME */ + while (1); + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/****************************************************************************** + * \name KdReceivePacket + * \brief Receive a packet from the KD port. + * \param [in] PacketType Describes the type of the packet to receive. + * This can be one of the PACKET_TYPE_ constants. + * \param [out] MessageHeader Pointer to a STRING structure for the header. + * \param [out] MessageData Pointer to a STRING structure for the data. + * \return KdPacketReceived if successful, KdPacketTimedOut if the receive + * timed out, KdPacketNeedsResend to signal that the last packet needs + * to be sent again. + * \note If PacketType is PACKET_TYPE_KD_POLL_BREAKIN, the function doesn't + * wait for any data, but returns KdPacketTimedOut instantly if no breakin + * packet byte is received. + * \sa http://www.nynaeve.net/?p=169 + */ +KDSTATUS +NTAPI +KdReceivePacket( + _In_ ULONG PacketType, + _Out_ PSTRING MessageHeader, + _Out_ PSTRING MessageData, + _Out_ PULONG DataLength, + _Inout_ PKD_CONTEXT KdContext) +{ + KDSTATUS Status; + DBGKD_MANIPULATE_STATE64* State; + + /* Special handling for breakin packet */ + if (PacketType == PACKET_TYPE_KD_POLL_BREAKIN) + { + return KdpPollBreakIn(); + } + + if (PacketType != PACKET_TYPE_KD_STATE_MANIPULATE) + { + /* What should we do ? */ + while (1); + } + + State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer; + + if (FakeNextManipulatePacket) + { + FakeNextManipulatePacket = FALSE; + *State = FakeManipulateState; + return KdPacketReceived; + } + + /* Receive data from GDB */ + Status = gdb_receive_packet(KdContext); + if (Status != KdPacketReceived) + return Status; + + /* Interpret it */ + return gdb_interpret_input(State, MessageData, DataLength, KdContext); +} + +VOID +NTAPI +KdSendPacket( + IN ULONG PacketType, + IN PSTRING MessageHeader, + IN PSTRING MessageData, + IN OUT PKD_CONTEXT KdContext) +{ + switch (PacketType) + { + case PACKET_TYPE_KD_STATE_CHANGE64: + send_kd_state_change((DBGKD_ANY_WAIT_STATE_CHANGE*)MessageHeader->Buffer); + return; + case PACKET_TYPE_KD_DEBUG_IO: + send_kd_debug_io((DBGKD_DEBUG_IO*)MessageHeader->Buffer, MessageData); + break; + case PACKET_TYPE_KD_STATE_MANIPULATE: + send_kd_state_manipulate((DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer, MessageData); + break; + default: + /* FIXME */ + while (1); + } +} + +/* EOF */