mirror of
https://github.com/reactos/reactos.git
synced 2024-12-27 01:24:38 +00:00
Added GNU Debugger stub for remote debugging.
Fixed bugs in NtQueryDirectoryObject. Added static library target. svn path=/trunk/; revision=2546
This commit is contained in:
parent
198a544138
commit
5a6adb4f13
25 changed files with 1734 additions and 147 deletions
|
@ -1,7 +1,4 @@
|
|||
file ntoskrnl/ntoskrnl.nostrip.exe
|
||||
#add-symbol-file lib/ntdll/ntdll.dll 0x77f61000
|
||||
#add-symbol-file lib/kernel32/kernel32.dll 0x77f01000
|
||||
#add-symbol-file apps/exp/exp.exe 0x401000
|
||||
#add-symbol-file subsys/csrss/csrss.exe 0x401000
|
||||
#add-symbol-file subsys/smss/smss.exe 0x401000
|
||||
break exp.c:254
|
||||
directory /mnt/windows/CvsHome/reactos/ntoskrnl
|
||||
symbol-file ntoskrnl/ntoskrnl.nostrip.exe -mapped
|
||||
set remotebaud 115200
|
||||
target remote com2
|
||||
|
|
|
@ -10,6 +10,13 @@ ARCH := i386
|
|||
#
|
||||
KDBG := 0
|
||||
|
||||
#
|
||||
# Whether to dump debug prints to the serial port
|
||||
# If this is disabled and KDBG is enabled the GDB
|
||||
# stub is used
|
||||
#
|
||||
SERDUMP := 0
|
||||
|
||||
#
|
||||
# Whether to compile for debugging
|
||||
#
|
||||
|
|
4
reactos/gdb.ini
Normal file
4
reactos/gdb.ini
Normal file
|
@ -0,0 +1,4 @@
|
|||
directory /mnt/windows/CvsHome/reactos/ntoskrnl
|
||||
symbol-file ntoskrnl/ntoskrnl.nostrip.exe -mapped
|
||||
set remotebaud 115200
|
||||
target remote com2
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: hal.c,v 1.1 2001/08/21 20:18:26 chorns Exp $
|
||||
/* $Id: hal.c,v 1.2 2002/01/23 23:39:24 chorns Exp $
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS kernel
|
||||
|
@ -645,6 +645,22 @@ KdPortSave(VOID)
|
|||
}
|
||||
|
||||
|
||||
BOOLEAN
|
||||
STDCALL
|
||||
KdPortDisableInterrupts()
|
||||
{
|
||||
UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN
|
||||
STDCALL
|
||||
KdPortEnableInterrupts()
|
||||
{
|
||||
UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
|
||||
VOID
|
||||
STDCALL
|
||||
KeAcquireSpinLock(
|
||||
|
|
|
@ -65,6 +65,8 @@ KdPortPollByte@4
|
|||
KdPortPutByte@4
|
||||
KdPortRestore@0
|
||||
KdPortSave@0
|
||||
KdPortDisableInterrupts@0
|
||||
KdPortEnableInterrupts@0
|
||||
KeAcquireSpinLock@8
|
||||
@KeAcquireSpinLockRaiseToSynch@4
|
||||
KeFlushWriteBuffer@0
|
||||
|
|
|
@ -65,6 +65,8 @@ KdPortPollByte=KdPortPollByte@4
|
|||
KdPortPutByte=KdPortPutByte@4
|
||||
KdPortRestore=KdPortRestore@0
|
||||
KdPortSave=KdPortSave@0
|
||||
KdPortDisableInterrupts=KdPortDisableInterrupts@0
|
||||
KdPortEnableInterrupts=KdPortEnableInterrupts@0
|
||||
KeAcquireSpinLock=KeAcquireSpinLock@8
|
||||
KeAcquireSpinLockRaiseToSynch=@KeAcquireSpinLockRaiseToSynch@4
|
||||
KeFlushWriteBuffer=KeFlushWriteBuffer@0
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: kdbg.c,v 1.2 2001/08/27 01:23:50 ekohl Exp $
|
||||
/* $Id: kdbg.c,v 1.3 2002/01/23 23:39:24 chorns Exp $
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS kernel
|
||||
|
@ -27,6 +27,11 @@
|
|||
#define SER_THR(x) ((x)+0)
|
||||
#define SER_DLL(x) ((x)+0)
|
||||
#define SER_IER(x) ((x)+1)
|
||||
#define SR_IER_ERDA 0x01
|
||||
#define SR_IER_ETHRE 0x02
|
||||
#define SR_IER_ERLSI 0x04
|
||||
#define SR_IER_EMS 0x08
|
||||
#define SR_IER_ALL 0x0F
|
||||
#define SER_DLM(x) ((x)+1)
|
||||
#define SER_IIR(x) ((x)+2)
|
||||
#define SER_LCR(x) ((x)+3)
|
||||
|
@ -46,6 +51,9 @@
|
|||
#define SER_MCR(x) ((x)+4)
|
||||
#define SR_MCR_DTR 0x01
|
||||
#define SR_MCR_RTS 0x02
|
||||
#define SR_MCR_OUT1 0x04
|
||||
#define SR_MCR_OUT2 0x08
|
||||
#define SR_MCR_LOOP 0x10
|
||||
#define SER_LSR(x) ((x)+5)
|
||||
#define SR_LSR_DR 0x01
|
||||
#define SR_LSR_TBE 0x20
|
||||
|
@ -331,4 +339,49 @@ KdPortSave (
|
|||
}
|
||||
|
||||
|
||||
/* HAL.KdPortDisableInterrupts */
|
||||
BOOLEAN
|
||||
STDCALL
|
||||
KdPortDisableInterrupts()
|
||||
{
|
||||
UCHAR ch;
|
||||
|
||||
if (PortInitialized == FALSE)
|
||||
return FALSE;
|
||||
|
||||
ch = READ_PORT_UCHAR (SER_MCR (PortBase));
|
||||
ch &= (~(SR_MCR_OUT1 | SR_MCR_OUT2));
|
||||
WRITE_PORT_UCHAR (SER_MCR (PortBase), ch);
|
||||
|
||||
ch = READ_PORT_UCHAR (SER_IER (PortBase));
|
||||
ch &= (~SR_IER_ALL);
|
||||
WRITE_PORT_UCHAR (SER_IER (PortBase), ch);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* HAL.KdPortEnableInterrupts */
|
||||
BOOLEAN
|
||||
STDCALL
|
||||
KdPortEnableInterrupts()
|
||||
{
|
||||
UCHAR ch;
|
||||
|
||||
if (PortInitialized == FALSE)
|
||||
return FALSE;
|
||||
|
||||
ch = READ_PORT_UCHAR (SER_IER (PortBase));
|
||||
ch &= (~SR_IER_ALL);
|
||||
ch |= SR_IER_ERDA;
|
||||
WRITE_PORT_UCHAR (SER_IER (PortBase), ch);
|
||||
|
||||
ch = READ_PORT_UCHAR (SER_MCR (PortBase));
|
||||
ch &= (~SR_MCR_LOOP);
|
||||
ch |= (SR_MCR_OUT1 | SR_MCR_OUT2);
|
||||
WRITE_PORT_UCHAR (SER_MCR (PortBase), ch);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#ifndef __INCLUDE_DDK_KDFUNCS_H
|
||||
#define __INCLUDE_DDK_KDFUNCS_H
|
||||
/* $Id: kdfuncs.h,v 1.4 2001/08/21 20:13:05 chorns Exp $ */
|
||||
/* $Id: kdfuncs.h,v 1.5 2002/01/23 23:39:24 chorns Exp $ */
|
||||
|
||||
/* --- NTOSKRNL.EXE --- */
|
||||
#if defined(__NTOSKRNL__)
|
||||
|
@ -50,4 +50,16 @@ KdPortSave (
|
|||
VOID
|
||||
);
|
||||
|
||||
BOOLEAN
|
||||
STDCALL
|
||||
KdPortDisableInterrupts(
|
||||
VOID
|
||||
);
|
||||
|
||||
BOOLEAN
|
||||
STDCALL
|
||||
KdPortEnableInterrupts(
|
||||
VOID
|
||||
);
|
||||
|
||||
#endif /* __INCLUDE_DDK_KDFUNCS_H */
|
||||
|
|
|
@ -111,7 +111,7 @@ typedef struct _DISPATCHER_HEADER
|
|||
UCHAR Inserted;
|
||||
LONG SignalState;
|
||||
LIST_ENTRY WaitListHead;
|
||||
} __attribute__((packed)) DISPATCHER_HEADER;
|
||||
} __attribute__((packed)) DISPATCHER_HEADER, *PDISPATCHER_HEADER;
|
||||
|
||||
|
||||
typedef struct _KQUEUE
|
||||
|
|
|
@ -28,7 +28,7 @@ struct _EXCEPTION_REGISTRATION;
|
|||
typedef EXCEPTION_DISPOSITION CDECL (*PEXCEPTION_HANDLER)(
|
||||
struct _EXCEPTION_RECORD* ExceptionRecord,
|
||||
struct _EXCEPTION_REGISTRATION* ExceptionRegistration,
|
||||
struct _CONTEXT* Context,
|
||||
PCONTEXT Context,
|
||||
PVOID DispatcherContext);
|
||||
|
||||
|
||||
|
|
|
@ -226,8 +226,6 @@ typedef unsigned short *PWCHAR;
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
typedef struct _FLOATING_SAVE_AREA {
|
||||
DWORD ControlWord;
|
||||
DWORD StatusWord;
|
||||
|
@ -240,7 +238,7 @@ typedef struct _FLOATING_SAVE_AREA {
|
|||
DWORD Cr0NpxState;
|
||||
} FLOATING_SAVE_AREA;
|
||||
|
||||
typedef struct _CONTEXT {
|
||||
typedef struct _CONTEXT_X86 {
|
||||
DWORD ContextFlags;
|
||||
|
||||
DWORD Dr0;
|
||||
|
@ -270,11 +268,9 @@ typedef struct _CONTEXT {
|
|||
DWORD EFlags;
|
||||
DWORD Esp;
|
||||
DWORD SegSs;
|
||||
} CONTEXT, *PCONTEXT, *LPCONTEXT;
|
||||
} CONTEXT_X86, *PCONTEXT_X86, *LPCONTEXT_X86;
|
||||
|
||||
#else /* __ppc__ */
|
||||
|
||||
typedef struct
|
||||
typedef struct _CONTEXT_PPC
|
||||
{
|
||||
/* Floating point registers returned when CONTEXT_FLOATING_POINT is set */
|
||||
double Fpr0;
|
||||
|
@ -367,7 +363,20 @@ typedef struct
|
|||
DWORD Dr5; /* Breakpoint Register 6 */
|
||||
DWORD Dr6; /* Debug Status Register */
|
||||
DWORD Dr7; /* Debug Control Register */
|
||||
} CONTEXT, *PCONTEXT, *LPCONTEXT;
|
||||
} CONTEXT_PPC, *PCONTEXT_PPC, *LPCONTEXT_PPC;
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
typedef CONTEXT_X86 CONTEXT;
|
||||
typedef PCONTEXT_X86 PCONTEXT;
|
||||
typedef LPCONTEXT_X86 LPCONTEXT;
|
||||
|
||||
#else /* __ppc__ */
|
||||
|
||||
typedef CONTEXT_PPC CONTEXT;
|
||||
typedef PCONTEXT_PPC PCONTEXT;
|
||||
typedef LPCONTEXT_PPC LPCONTEXT;
|
||||
|
||||
#endif
|
||||
|
||||
typedef HANDLE *PHANDLE;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $Id: Makefile,v 1.64 2002/01/21 22:34:36 hbirr Exp $
|
||||
# $Id: Makefile,v 1.65 2002/01/23 23:39:24 chorns Exp $
|
||||
#
|
||||
# ReactOS Operating System
|
||||
#
|
||||
|
@ -321,7 +321,8 @@ OBJECTS_CC = \
|
|||
OBJECTS_KD = \
|
||||
kd/kdebug.o \
|
||||
kd/service.o \
|
||||
kd/dlog.o
|
||||
kd/dlog.o \
|
||||
kd/gdbstub.o
|
||||
|
||||
DEP_OBJECTS := $(OBJECTS_NT) $(OBJECTS_MM) $(OBJECTS_ARCH) \
|
||||
$(OBJECTS_IO) $(OBJECTS_KE) $(OBJECTS_OB) \
|
||||
|
|
|
@ -739,7 +739,6 @@ CmiAddValueToKey(IN PREGISTRY_FILE RegistryFile,
|
|||
>= ((LONG)(ValueListBlock->SubBlockSize-4))/(LONG)sizeof(BLOCK_OFFSET))
|
||||
// >= -(ValueListBlock->SubBlockSize-4)/sizeof(BLOCK_OFFSET))
|
||||
{
|
||||
DPRINT1("\n");
|
||||
Status = CmiAllocateBlock(RegistryFile,
|
||||
(PVOID) &NewValueListBlock,
|
||||
sizeof(BLOCK_OFFSET) *
|
||||
|
@ -760,7 +759,7 @@ CmiAddValueToKey(IN PREGISTRY_FILE RegistryFile,
|
|||
KeyBlock->ValuesOffset = VLBOffset;
|
||||
ValueListBlock = NewValueListBlock;
|
||||
}
|
||||
DPRINT1("KeyBlock->NumberOfValues %d, ValueListBlock->SubBlockSize %d (%d %x)\n",
|
||||
DPRINT("KeyBlock->NumberOfValues %d, ValueListBlock->SubBlockSize %d (%d %x)\n",
|
||||
KeyBlock->NumberOfValues, ValueListBlock->SubBlockSize,
|
||||
-(ValueListBlock->SubBlockSize-4)/sizeof(BLOCK_OFFSET),
|
||||
-(ValueListBlock->SubBlockSize-4)/sizeof(BLOCK_OFFSET));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: kd.h,v 1.3 2001/02/10 22:51:08 dwelch Exp $
|
||||
/* $Id: kd.h,v 1.4 2002/01/23 23:39:25 chorns Exp $
|
||||
*
|
||||
* kernel debugger prototypes
|
||||
*/
|
||||
|
@ -6,6 +6,15 @@
|
|||
#ifndef __INCLUDE_INTERNAL_KERNEL_DEBUGGER_H
|
||||
#define __INCLUDE_INTERNAL_KERNEL_DEBUGGER_H
|
||||
|
||||
#include <internal/ke.h>
|
||||
|
||||
typedef enum _KD_CONTINUE_TYPE
|
||||
{
|
||||
kdContinue = 0,
|
||||
kdDoNotHandleException,
|
||||
kdHandleException
|
||||
} KD_CONTINUE_TYPE;
|
||||
|
||||
ULONG
|
||||
KdpPrintString (PANSI_STRING String);
|
||||
|
||||
|
@ -16,4 +25,27 @@ DebugLogInit(VOID);
|
|||
VOID
|
||||
DebugLogInit2(VOID);
|
||||
|
||||
VOID
|
||||
KdInit1();
|
||||
|
||||
VOID
|
||||
KdInit2();
|
||||
|
||||
VOID
|
||||
KdPutChar(UCHAR Value);
|
||||
|
||||
UCHAR
|
||||
KdGetChar();
|
||||
|
||||
VOID
|
||||
KdGdbStubInit();
|
||||
|
||||
VOID
|
||||
KdDebugPrint (LPSTR Message);
|
||||
|
||||
KD_CONTINUE_TYPE
|
||||
KdEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord,
|
||||
PCONTEXT Context,
|
||||
PKTRAP_FRAME TrapFrame);
|
||||
|
||||
#endif /* __INCLUDE_INTERNAL_KERNEL_DEBUGGER_H */
|
||||
|
|
|
@ -100,6 +100,8 @@ NTSTATUS LdrFindModuleObject(
|
|||
PUNICODE_STRING ModuleName,
|
||||
PMODULE_OBJECT *ModuleObject);
|
||||
|
||||
NTSTATUS LdrpOpenModuleDirectory(PHANDLE Handle);
|
||||
|
||||
extern ULONG_PTR LdrHalBase;
|
||||
|
||||
#endif /* __INCLUDE_INTERNAL_LDR_H */
|
||||
|
|
1325
reactos/ntoskrnl/kd/gdbstub.c
Normal file
1325
reactos/ntoskrnl/kd/gdbstub.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
/* $Id: kdebug.c,v 1.30 2001/12/31 19:06:47 dwelch Exp $
|
||||
/* $Id: kdebug.c,v 1.31 2002/01/23 23:39:25 chorns Exp $
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS kernel
|
||||
|
@ -18,7 +18,9 @@
|
|||
|
||||
/* serial debug connection */
|
||||
#define DEFAULT_DEBUG_PORT 2 /* COM2 */
|
||||
#define DEFAULT_DEBUG_BAUD_RATE 19200 /* 19200 Baud */
|
||||
#define DEFAULT_DEBUG_COM1_IRQ 4 /* COM1 IRQ */
|
||||
#define DEFAULT_DEBUG_COM2_IRQ 3 /* COM2 IRQ */
|
||||
#define DEFAULT_DEBUG_BAUD_RATE 115200 /* 115200 Baud */
|
||||
|
||||
/* bochs debug output */
|
||||
#define BOCHS_LOGGER_PORT (0xe9)
|
||||
|
@ -43,8 +45,8 @@ KdDebuggerNotPresent = TRUE; /* EXPORTED */
|
|||
|
||||
|
||||
static BOOLEAN KdpBreakPending = FALSE;
|
||||
static BOOLEAN KdpBreakRecieved = FALSE;
|
||||
static ULONG KdpDebugType = ScreenDebug | BochsDebug;
|
||||
ULONG KdpPortIrq = 0;
|
||||
|
||||
/* PRIVATE FUNCTIONS ********************************************************/
|
||||
|
||||
|
@ -75,12 +77,12 @@ KdInitSystem (
|
|||
#ifdef KDBG
|
||||
/* Initialize runtime debugging if available */
|
||||
DbgRDebugInit();
|
||||
|
||||
#endif
|
||||
|
||||
/* set debug port default values */
|
||||
PortInfo.ComPort = DEFAULT_DEBUG_PORT;
|
||||
PortInfo.BaudRate = DEFAULT_DEBUG_BAUD_RATE;
|
||||
KdpPortIrq = DEFAULT_DEBUG_COM2_IRQ;
|
||||
|
||||
/*
|
||||
* parse kernel command line
|
||||
|
@ -157,6 +159,21 @@ KdInitSystem (
|
|||
PortInfo.BaudRate = Value;
|
||||
}
|
||||
}
|
||||
else if (!_strnicmp (p2, "IRQ", 3))
|
||||
{
|
||||
p2 += 3;
|
||||
if (*p2 != '=')
|
||||
{
|
||||
p2++;
|
||||
Value = (ULONG)atol (p2);
|
||||
if (Value > 0)
|
||||
{
|
||||
KdDebuggerEnabled = TRUE;
|
||||
KdpDebugType = KdpDebugType | SerialDebug;
|
||||
KdpPortIrq = Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
|
@ -200,6 +217,31 @@ KdInitSystem (
|
|||
}
|
||||
}
|
||||
|
||||
VOID KdInit1()
|
||||
{
|
||||
#ifndef SERDUMP
|
||||
|
||||
/* Initialize kernel debugger */
|
||||
if (KdDebuggerEnabled && (KdpDebugType & SerialDebug))
|
||||
{
|
||||
KdGdbStubInit(0);
|
||||
}
|
||||
|
||||
#endif /* !SERDUMP */
|
||||
}
|
||||
|
||||
VOID KdInit2()
|
||||
{
|
||||
#ifndef SERDUMP
|
||||
|
||||
/* Initialize kernel debugger */
|
||||
if (KdDebuggerEnabled && (KdpDebugType & SerialDebug))
|
||||
{
|
||||
KdGdbStubInit(1);
|
||||
}
|
||||
|
||||
#endif /* !SERDUMP */
|
||||
}
|
||||
|
||||
ULONG KdpPrintString (PANSI_STRING String)
|
||||
{
|
||||
|
@ -210,17 +252,7 @@ ULONG KdpPrintString (PANSI_STRING String)
|
|||
HalDisplayString (String->Buffer);
|
||||
}
|
||||
if (KdpDebugType & SerialDebug)
|
||||
{
|
||||
while (*pch != 0)
|
||||
{
|
||||
if (*pch == '\n')
|
||||
{
|
||||
KdPortPutByte ('\r');
|
||||
}
|
||||
KdPortPutByte (*pch);
|
||||
pch++;
|
||||
}
|
||||
}
|
||||
KdDebugPrint (pch);
|
||||
if (KdpDebugType & BochsDebug)
|
||||
{
|
||||
pch = String->Buffer;
|
||||
|
@ -253,37 +285,18 @@ KdPollBreakIn (
|
|||
VOID
|
||||
)
|
||||
{
|
||||
BOOLEAN Result = FALSE;
|
||||
UCHAR ByteRead;
|
||||
#ifndef SERDUMP
|
||||
|
||||
if (KdDebuggerEnabled == FALSE || KdpDebugType != SerialDebug)
|
||||
return Result;
|
||||
if (!KdDebuggerEnabled || !(KdpDebugType & SerialDebug))
|
||||
return FALSE;
|
||||
|
||||
// Flags = KiDisableInterrupts();
|
||||
return TRUE;
|
||||
|
||||
HalDisplayString ("Waiting for kernel debugger connection...\n");
|
||||
#else /* SERDUMP */
|
||||
|
||||
if (KdPortPollByte (&ByteRead))
|
||||
{
|
||||
if (ByteRead == 0x62)
|
||||
{
|
||||
if (KdpBreakPending == TRUE)
|
||||
{
|
||||
KdpBreakPending = FALSE;
|
||||
KdpBreakRecieved = TRUE;
|
||||
Result = TRUE;
|
||||
}
|
||||
HalDisplayString (" Kernel debugger connected\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
HalDisplayString (" Kernel debugger connection failed\n");
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
// KiRestoreInterrupts (Flags);
|
||||
|
||||
return Result;
|
||||
#endif /* !SERDUMP */
|
||||
}
|
||||
|
||||
VOID STDCALL
|
||||
|
@ -368,4 +381,24 @@ KdSystemDebugControl(ULONG Code)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Support routines for the GDB stubs */
|
||||
|
||||
VOID
|
||||
KdPutChar(UCHAR Value)
|
||||
{
|
||||
KdPortPutByte (Value);
|
||||
}
|
||||
|
||||
|
||||
UCHAR
|
||||
KdGetChar()
|
||||
{
|
||||
UCHAR Value;
|
||||
|
||||
while (!KdPortGetByte (&Value));
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/* $Id: catch.c,v 1.16 2001/09/24 00:51:16 chorns Exp $
|
||||
/* $Id: catch.c,v 1.17 2002/01/23 23:39:25 chorns Exp $
|
||||
*
|
||||
* PROJECT: ReactOS kernel
|
||||
* FILE: ntoskrnl/ke/catch.c
|
||||
|
@ -27,9 +27,11 @@
|
|||
/* INCLUDES *****************************************************************/
|
||||
|
||||
#include <ddk/ntddk.h>
|
||||
#include <roscfg.h>
|
||||
#include <internal/ke.h>
|
||||
#include <internal/ldr.h>
|
||||
#include <internal/ps.h>
|
||||
#include <internal/kd.h>
|
||||
|
||||
#define NDEBUG
|
||||
#include <internal/debug.h>
|
||||
|
@ -197,7 +199,7 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
EXCEPTION_DISPOSITION Value;
|
||||
CONTEXT TContext;
|
||||
|
||||
DPRINT("KiDispatchException() called \n");
|
||||
DPRINT("KiDispatchException() called\n");
|
||||
/* PCR->KeExceptionDispatchCount++; */
|
||||
|
||||
if (Context == NULL)
|
||||
|
@ -212,12 +214,12 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
|
||||
Context = &TContext;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT)
|
||||
{
|
||||
Context->Eip--;
|
||||
}
|
||||
|
||||
#endif
|
||||
if (PreviousMode == UserMode)
|
||||
{
|
||||
if (SearchFrames)
|
||||
|
@ -265,15 +267,31 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
}
|
||||
else
|
||||
{
|
||||
KD_CONTINUE_TYPE Action;
|
||||
|
||||
/* PreviousMode == KernelMode */
|
||||
Value = RtlpDispatchException(ExceptionRecord, Context);
|
||||
|
||||
DPRINT("RtlpDispatchException() returned with 0x%X\n", Value);
|
||||
#ifndef KDBG
|
||||
|
||||
/* If RtlpDispatchException() does not handle the exception then bugcheck */
|
||||
if (Value != ExceptionContinueExecution)
|
||||
KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED);
|
||||
|
||||
#endif /* KDBG */
|
||||
|
||||
Action = KdEnterDebuggerException (ExceptionRecord, Context, Tf);
|
||||
if (Action != kdHandleException)
|
||||
{
|
||||
Value = RtlpDispatchException (ExceptionRecord, Context);
|
||||
|
||||
DPRINT("RtlpDispatchException() returned with 0x%X\n", Value);
|
||||
/* If RtlpDispatchException() does not handle the exception then bugcheck */
|
||||
if (Value != ExceptionContinueExecution)
|
||||
{
|
||||
KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED);
|
||||
KeContextToTrapFrame (Context, KeGetCurrentThread()->TrapFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,6 +95,29 @@ static char *ExceptionTypeStrings[] =
|
|||
"Machine Check"
|
||||
};
|
||||
|
||||
static NTSTATUS ExceptionToNtStatus[] =
|
||||
{
|
||||
STATUS_INTEGER_DIVIDE_BY_ZERO,
|
||||
STATUS_SINGLE_STEP,
|
||||
STATUS_ACCESS_VIOLATION,
|
||||
STATUS_BREAKPOINT,
|
||||
STATUS_INTEGER_OVERFLOW,
|
||||
STATUS_ARRAY_BOUNDS_EXCEEDED,
|
||||
STATUS_ILLEGAL_INSTRUCTION,
|
||||
STATUS_ACCESS_VIOLATION, /* STATUS_FLT_INVALID_OPERATION */
|
||||
STATUS_ACCESS_VIOLATION,
|
||||
STATUS_ACCESS_VIOLATION,
|
||||
STATUS_ACCESS_VIOLATION,
|
||||
STATUS_ACCESS_VIOLATION,
|
||||
STATUS_STACK_OVERFLOW,
|
||||
STATUS_ACCESS_VIOLATION,
|
||||
STATUS_ACCESS_VIOLATION,
|
||||
STATUS_ACCESS_VIOLATION, /* STATUS_FLT_INVALID_OPERATION */
|
||||
STATUS_DATATYPE_MISALIGNMENT,
|
||||
STATUS_ACCESS_VIOLATION
|
||||
};
|
||||
|
||||
|
||||
/* FUNCTIONS ****************************************************************/
|
||||
|
||||
extern unsigned int _text_start__, _text_end__;
|
||||
|
@ -168,37 +191,10 @@ KiKernelTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr, PVOID Cr2)
|
|||
{
|
||||
EXCEPTION_RECORD Er;
|
||||
|
||||
if (ExceptionNr == 0)
|
||||
{
|
||||
Er.ExceptionCode = STATUS_INTEGER_DIVIDE_BY_ZERO;
|
||||
}
|
||||
else if (ExceptionNr == 1)
|
||||
{
|
||||
Er.ExceptionCode = STATUS_SINGLE_STEP;
|
||||
}
|
||||
else if (ExceptionNr == 3)
|
||||
{
|
||||
Er.ExceptionCode = STATUS_BREAKPOINT;
|
||||
}
|
||||
else if (ExceptionNr == 4)
|
||||
{
|
||||
Er.ExceptionCode = STATUS_INTEGER_OVERFLOW;
|
||||
}
|
||||
else if (ExceptionNr == 5)
|
||||
{
|
||||
Er.ExceptionCode = STATUS_ARRAY_BOUNDS_EXCEEDED;
|
||||
}
|
||||
else if (ExceptionNr == 6)
|
||||
{
|
||||
Er.ExceptionCode = STATUS_ILLEGAL_INSTRUCTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
Er.ExceptionCode = STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
Er.ExceptionFlags = 0;
|
||||
Er.ExceptionRecord = NULL;
|
||||
Er.ExceptionAddress = (PVOID)Tf->Eip;
|
||||
|
||||
if (ExceptionNr == 14)
|
||||
{
|
||||
Er.NumberParameters = 2;
|
||||
|
@ -207,6 +203,14 @@ KiKernelTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr, PVOID Cr2)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (ExceptionNr < 16)
|
||||
{
|
||||
Er.ExceptionCode = ExceptionToNtStatus[ExceptionNr];
|
||||
}
|
||||
else
|
||||
{
|
||||
Er.ExceptionCode = STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
Er.NumberParameters = 0;
|
||||
}
|
||||
|
||||
|
@ -406,7 +410,7 @@ KiTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr)
|
|||
* Complete CPU context
|
||||
*/
|
||||
{
|
||||
//#define SEH
|
||||
#define SEH
|
||||
unsigned int cr2;
|
||||
#ifndef SEH
|
||||
unsigned int cr3;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: irq.c,v 1.15 2001/08/30 20:38:19 dwelch Exp $
|
||||
/* $Id: irq.c,v 1.16 2002/01/23 23:39:25 chorns Exp $
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS kernel
|
||||
|
@ -240,6 +240,30 @@ typedef struct _KIRQ_TRAPFRAME
|
|||
ULONG Eflags;
|
||||
} KIRQ_TRAPFRAME, *PKIRQ_TRAPFRAME;
|
||||
|
||||
#ifdef DBG
|
||||
|
||||
VOID
|
||||
KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame,
|
||||
PKTRAP_FRAME TrapFrame)
|
||||
{
|
||||
TrapFrame->Fs = IrqTrapFrame->Fs;
|
||||
TrapFrame->Fs = IrqTrapFrame->Es;
|
||||
TrapFrame->Ds = IrqTrapFrame->Ds;
|
||||
TrapFrame->Eax = IrqTrapFrame->Eax;
|
||||
TrapFrame->Ecx = IrqTrapFrame->Ecx;
|
||||
TrapFrame->Edx = IrqTrapFrame->Edx;
|
||||
TrapFrame->Ebx = IrqTrapFrame->Ebx;
|
||||
TrapFrame->Esp = IrqTrapFrame->Esp;
|
||||
TrapFrame->Ebp = IrqTrapFrame->Ebp;
|
||||
TrapFrame->Esi = IrqTrapFrame->Esi;
|
||||
TrapFrame->Edi = IrqTrapFrame->Edi;
|
||||
TrapFrame->Eip = IrqTrapFrame->Eip;
|
||||
TrapFrame->Cs = IrqTrapFrame->Cs;
|
||||
TrapFrame->Eflags = IrqTrapFrame->Eflags;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MP
|
||||
|
||||
VOID
|
||||
|
@ -256,6 +280,15 @@ KiInterruptDispatch (ULONG Vector, PKIRQ_TRAPFRAME Trapframe)
|
|||
PLIST_ENTRY current;
|
||||
ULONG irq;
|
||||
|
||||
#ifdef DBG
|
||||
|
||||
KTRAP_FRAME KernelTrapFrame;
|
||||
|
||||
KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
|
||||
KeGetCurrentThread()->TrapFrame = &KernelTrapFrame;
|
||||
|
||||
#endif /* DBG */
|
||||
|
||||
DPRINT("I(%d) ", Vector);
|
||||
|
||||
/*
|
||||
|
@ -348,6 +381,15 @@ KiInterruptDispatch (ULONG irq, PKIRQ_TRAPFRAME Trapframe)
|
|||
PKINTERRUPT isr;
|
||||
PLIST_ENTRY current;
|
||||
|
||||
#ifdef DBG
|
||||
|
||||
KTRAP_FRAME KernelTrapFrame;
|
||||
|
||||
KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
|
||||
KeGetCurrentThread()->TrapFrame = &KernelTrapFrame;
|
||||
|
||||
#endif /* DBG */
|
||||
|
||||
/*
|
||||
* Notify the rest of the kernel of the raised irq level
|
||||
*/
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/* $Id: main.c,v 1.111 2002/01/15 02:51:32 dwelch Exp $
|
||||
/* $Id: main.c,v 1.112 2002/01/23 23:39:25 chorns Exp $
|
||||
*
|
||||
* PROJECT: ReactOS kernel
|
||||
* FILE: ntoskrnl/ke/main.c
|
||||
|
@ -88,7 +88,7 @@ SERVICE Services[] = {
|
|||
{L"pci", L"PCI Bus Driver", L"Boot Bus Extender", 0, 1},
|
||||
{L"keyboard", L"Standard Keyboard Driver", L"Base", 0, 1},
|
||||
{L"blue", L"Bluescreen Driver", L"Base", 0, 1},
|
||||
/* {L"vidport", L"Video Port Driver", L"Base", 0, 1},
|
||||
{L"vidport", L"Video Port Driver", L"Base", 0, 1},
|
||||
{L"vgamp", L"VGA Miniport", L"Base", 0, 1},
|
||||
{L"minixfs", L"Minix File System", L"File system", 0, 1},
|
||||
{L"msfs", L"Mail Slot File System", L"File system", 0, 1},
|
||||
|
@ -97,13 +97,13 @@ SERVICE Services[] = {
|
|||
{L"mouclass", L"Mouse Class Driver", L"Pointer Class", 0, 1},
|
||||
{L"ndis", L"NDIS System Driver", L"NDIS Wrapper", 0, 1},
|
||||
{L"ne2000", L"Novell Eagle 2000 Driver", L"NDIS", 0, 1},
|
||||
{L"afd", L"AFD Networking Support Environment", L"TDI", 0, 1},*/
|
||||
{L"afd", L"AFD Networking Support Environment", L"TDI", 0, 1},
|
||||
{NULL,}
|
||||
};
|
||||
|
||||
/* FUNCTIONS ****************************************************************/
|
||||
|
||||
//#define FULLREG
|
||||
#define FULLREG
|
||||
|
||||
VOID CreateDefaultRegistryForLegacyDriver(
|
||||
PSERVICE Service)
|
||||
|
@ -312,7 +312,7 @@ VOID CreateDefaultRegistryForLegacyDriver(
|
|||
NtClose(KeyHandle);
|
||||
return;
|
||||
}
|
||||
#if FULLREG
|
||||
#ifdef FULLREG
|
||||
DwordData = Service->Start;
|
||||
Length = sizeof(DWORD);
|
||||
Status = RtlWriteRegistryValue(
|
||||
|
@ -953,11 +953,7 @@ ExpInitializeExecutive(VOID)
|
|||
* Initialize the kernel debugger
|
||||
*/
|
||||
KdInitSystem (0, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock);
|
||||
if (KdPollBreakIn ())
|
||||
{
|
||||
DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C);
|
||||
}
|
||||
|
||||
|
||||
MmInit2();
|
||||
KeInit2();
|
||||
|
||||
|
@ -965,7 +961,14 @@ ExpInitializeExecutive(VOID)
|
|||
|
||||
ObInit();
|
||||
PiInitProcessManager();
|
||||
|
||||
|
||||
KdInit1();
|
||||
|
||||
if (KdPollBreakIn ())
|
||||
{
|
||||
DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C);
|
||||
}
|
||||
|
||||
/*
|
||||
* Display version number and copyright/warranty message
|
||||
*/
|
||||
|
@ -1029,6 +1032,7 @@ ExpInitializeExecutive(VOID)
|
|||
NtInit();
|
||||
MmInit3();
|
||||
CcInit();
|
||||
KdInit2();
|
||||
|
||||
/* Report all resources used by hal */
|
||||
HalReportResourceUsage();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: loader.c,v 1.94 2002/01/02 21:00:55 chorns Exp $
|
||||
/* $Id: loader.c,v 1.95 2002/01/23 23:39:25 chorns Exp $
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS kernel
|
||||
|
@ -322,6 +322,24 @@ VOID LdrInitModuleManagement(VOID)
|
|||
ModuleObject->TextSection = &LdrHalTextSection;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS
|
||||
LdrpOpenModuleDirectory(PHANDLE Handle)
|
||||
{
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
UNICODE_STRING ModuleDirectory;
|
||||
|
||||
RtlInitUnicodeString (&ModuleDirectory, MODULE_ROOT_NAME);
|
||||
InitializeObjectAttributes(&ObjectAttributes,
|
||||
&ModuleDirectory,
|
||||
OBJ_CASE_INSENSITIVE,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
return NtOpenDirectoryObject(Handle, GENERIC_ALL, &ObjectAttributes);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* load the auto config drivers.
|
||||
*/
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/* $Id: mm.c,v 1.53 2002/01/01 00:21:56 dwelch Exp $
|
||||
/* $Id: mm.c,v 1.54 2002/01/23 23:39:26 chorns Exp $
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top directory
|
||||
* PROJECT: ReactOS kernel
|
||||
|
@ -328,7 +328,6 @@ NTSTATUS MmNotPresentFault(KPROCESSOR_MODE Mode,
|
|||
MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace, (PVOID)Address);
|
||||
if (MemoryArea == NULL)
|
||||
{
|
||||
DbgPrint("%s:%d\n",__FILE__,__LINE__);
|
||||
if (!FromMdl)
|
||||
{
|
||||
MmUnlockAddressSpace(AddressSpace);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: dirobj.c,v 1.12 2001/06/16 14:10:55 ekohl Exp $
|
||||
/* $Id: dirobj.c,v 1.13 2002/01/23 23:39:26 chorns Exp $
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS kernel
|
||||
|
@ -183,10 +183,10 @@ NTSTATUS STDCALL NtQueryDirectoryObject (IN HANDLE DirObjHandle,
|
|||
}
|
||||
else
|
||||
{
|
||||
DirectorySize = 1;
|
||||
DirectoryCount = 1;
|
||||
}
|
||||
DirectorySize = (DirectoryCount + 1) * sizeof (OBJDIR_INFORMATION);
|
||||
if (DirectorySize >= SpaceLeft)
|
||||
DirectorySize = DirectoryCount * sizeof (OBJDIR_INFORMATION);
|
||||
if (DirectorySize > SpaceLeft)
|
||||
{
|
||||
return (STATUS_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ NTSTATUS STDCALL NtQueryDirectoryObject (IN HANDLE DirObjHandle,
|
|||
);
|
||||
if ((EntriesToSkip) && (current_entry == & dir->head))
|
||||
{
|
||||
return (STATUS_INVALID_PARAMETER);
|
||||
return (STATUS_NO_MORE_ENTRIES);
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
@ -225,44 +225,44 @@ NTSTATUS STDCALL NtQueryDirectoryObject (IN HANDLE DirObjHandle,
|
|||
*/
|
||||
SpaceLeft -= DirectorySize;
|
||||
/* Scan the directory */
|
||||
do {
|
||||
do
|
||||
{
|
||||
/*
|
||||
* Check if we reached the end of the directory.
|
||||
*/
|
||||
if (current_entry == & dir->head)
|
||||
{
|
||||
/* Any data? */
|
||||
/* Any data? */
|
||||
if (i) break; /* DONE */
|
||||
/* FIXME: better error handling here! */
|
||||
return (STATUS_NO_MORE_ENTRIES);
|
||||
}
|
||||
/*
|
||||
/*
|
||||
* Compute the current OBJECT_HEADER memory
|
||||
* object's address.
|
||||
*/
|
||||
current = CONTAINING_RECORD(current_entry, OBJECT_HEADER, Entry);
|
||||
/*
|
||||
* Compute the space required in the user buffer to copy
|
||||
* the data from the current object:
|
||||
current = CONTAINING_RECORD(current_entry, OBJECT_HEADER, Entry);
|
||||
/*
|
||||
* Compute the space required in the user buffer to copy
|
||||
* the data from the current object:
|
||||
*
|
||||
* Name (WCHAR) 0 TypeName (WCHAR) 0
|
||||
*/
|
||||
*/
|
||||
NameLength = (wcslen (current->Name.Buffer) * sizeof (WCHAR));
|
||||
TypeNameLength = (wcslen (current->ObjectType->TypeName.Buffer) * sizeof (WCHAR));
|
||||
SpaceRequired =
|
||||
(NameLength + sizeof (WCHAR))
|
||||
+ (TypeNameLength + sizeof (WCHAR));
|
||||
SpaceRequired = (NameLength + 1) * sizeof (WCHAR)
|
||||
+ (TypeNameLength + 1) * sizeof (WCHAR);
|
||||
/*
|
||||
* Check for free space in the user buffer.
|
||||
*/
|
||||
if (SpaceRequired >= SpaceLeft)
|
||||
if (SpaceRequired > SpaceLeft)
|
||||
{
|
||||
return (STATUS_BUFFER_TOO_SMALL);
|
||||
}
|
||||
/*
|
||||
* Copy the current directory entry's data into the buffer
|
||||
/*
|
||||
* Copy the current directory entry's data into the buffer
|
||||
* and update the OBJDIR_INFORMATION entry in the array.
|
||||
*/
|
||||
*/
|
||||
/* --- Object's name --- */
|
||||
current_odi->ObjectName.Length = NameLength;
|
||||
current_odi->ObjectName.MaximumLength = (NameLength + sizeof (WCHAR));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $Id: helper.mk,v 1.10 2002/01/08 00:49:02 dwelch Exp $
|
||||
# $Id: helper.mk,v 1.11 2002/01/23 23:39:26 chorns Exp $
|
||||
#
|
||||
# Helper makefile for ReactOS modules
|
||||
# Variables this makefile accepts:
|
||||
|
@ -6,7 +6,7 @@
|
|||
# program = User mode program
|
||||
# proglib = Executable program that have exported functions
|
||||
# dynlink = Dynamic Link Library (DLL)
|
||||
# library = Import library that will be linked with other code
|
||||
# library = Library that will be linked with other code
|
||||
# driver_library = Import library for a driver
|
||||
# driver = Kernel mode driver
|
||||
# export_driver = Kernel mode driver that have exported functions
|
||||
|
@ -125,8 +125,8 @@ endif
|
|||
endif
|
||||
|
||||
ifeq ($(TARGET_TYPE),library)
|
||||
MK_MODE := user
|
||||
MK_EXETYPE := dll
|
||||
MK_MODE := static
|
||||
MK_EXETYPE :=
|
||||
MK_DEFEXT := .a
|
||||
MK_DEFENTRY :=
|
||||
MK_DDKLIBS :=
|
||||
|
@ -135,12 +135,12 @@ ifeq ($(TARGET_TYPE),library)
|
|||
MK_CPPFLAGS := -I./ -I$(SDK_PATH_INC)
|
||||
MK_RCFLAGS := --include-dir $(SDK_PATH_INC)
|
||||
MK_IMPLIB := no
|
||||
MK_IMPLIBONLY := yes
|
||||
MK_IMPLIBDEFPATH := $(SDK_PATH_LIB)
|
||||
MK_IMPLIB_EXT := .a
|
||||
MK_IMPLIBONLY := no
|
||||
MK_IMPLIBDEFPATH :=
|
||||
MK_IMPLIB_EXT :=
|
||||
MK_INSTALLDIR := $(SDK_PATH_INC)
|
||||
MK_DISTDIR := # FIXME
|
||||
MK_RES_BASE :=
|
||||
MK_RES_BASE :=
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_TYPE),driver_library)
|
||||
|
@ -523,6 +523,14 @@ $(MK_FULLNAME): $(MK_FULLRES) $(TARGET_OBJECTS) $(MK_LIBS)
|
|||
|
||||
endif # MK_MODE
|
||||
|
||||
# Static library target
|
||||
ifeq ($(MK_MODE),static)
|
||||
|
||||
$(MK_FULLNAME): $(TARGET_OBJECTS)
|
||||
$(AR) -r $(MK_FULLNAME) $(TARGET_OBJECTS)
|
||||
|
||||
endif # MK_MODE
|
||||
|
||||
endif # MK_IMPLIBONLY
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue