/*
 * PROJECT:         ReactOS Kernel
 * LICENSE:         GPL - See COPYING in the top level directory
 * FILE:            ntoskrnl/dbgk/dbgkutil.c
 * PURPOSE:         User-Mode Debugging Support, Internal Debug Functions.
 * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
 */

/* INCLUDES ******************************************************************/

#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>

/* FUNCTIONS *****************************************************************/

HANDLE
NTAPI
DbgkpSectionToFileHandle(IN PVOID Section)
{
    NTSTATUS Status;
    POBJECT_NAME_INFORMATION FileName;
    OBJECT_ATTRIBUTES ObjectAttributes;
    IO_STATUS_BLOCK IoStatusBlock;
    HANDLE Handle;
    PAGED_CODE();

    /* Get the filename of the section */
    Status = MmGetFileNameForSection(Section, &FileName);
    if (!NT_SUCCESS(Status)) return NULL;

    /* Initialize object attributes */
    InitializeObjectAttributes(&ObjectAttributes,
                               &FileName->Name,
                               OBJ_CASE_INSENSITIVE |
                               OBJ_FORCE_ACCESS_CHECK |
                               OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL);

    /* Open the file */
    Status = ZwOpenFile(&Handle,
                        GENERIC_READ | SYNCHRONIZE,
                        &ObjectAttributes,
                        &IoStatusBlock,
                        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                        FILE_SYNCHRONOUS_IO_NONALERT);

    /* Free the name and return the handle if we succeeded */
    ExFreePool(FileName);
    if (!NT_SUCCESS(Status)) return NULL;
    return Handle;
}

BOOLEAN
NTAPI
DbgkpSuspendProcess(VOID)
{
    PAGED_CODE();

    /* Make sure this isn't a deleted process */
    if (!PsGetCurrentProcess()->ProcessDelete)
    {
        /* Freeze all the threads */
        KeFreezeAllThreads();
        return TRUE;
    }
    else
    {
        /* No suspend was done */
        return FALSE;
    }
}

VOID
NTAPI
DbgkpResumeProcess(VOID)
{
    PAGED_CODE();

    /* Thaw all the threads */
    KeThawAllThreads();
}

VOID
NTAPI
DbgkCreateThread(IN PETHREAD Thread,
                 IN PVOID StartAddress)
{
    PEPROCESS Process = PsGetCurrentProcess();
    ULONG ProcessFlags;
    IMAGE_INFO ImageInfo;
    PIMAGE_NT_HEADERS NtHeader;
    POBJECT_NAME_INFORMATION ModuleName;
    UNICODE_STRING NtDllName;
    NTSTATUS Status;
    PVOID DebugPort;
    DBGKM_MSG ApiMessage;
    PDBGKM_CREATE_THREAD CreateThread = &ApiMessage.CreateThread;
    PDBGKM_CREATE_PROCESS CreateProcess = &ApiMessage.CreateProcess;
    PDBGKM_LOAD_DLL LoadDll = &ApiMessage.LoadDll;
    OBJECT_ATTRIBUTES ObjectAttributes;
    IO_STATUS_BLOCK IoStatusBlock;
    PTEB Teb;
    PAGED_CODE();

    /* Sanity check */
    ASSERT(Thread == PsGetCurrentThread());

    /* Try ORing in the create reported and image notify flags */
    ProcessFlags = PspSetProcessFlag(Process,
                                     PSF_CREATE_REPORTED_BIT |
                                     PSF_IMAGE_NOTIFY_DONE_BIT);

    /* Check if we were the first to set them or if another thread raced us */
    if (!(ProcessFlags & PSF_IMAGE_NOTIFY_DONE_BIT) && (PsImageNotifyEnabled))
    {
        /* It hasn't.. set up the image info for the process */
        ImageInfo.Properties = 0;
        ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
        ImageInfo.ImageBase = Process->SectionBaseAddress;
        ImageInfo.ImageSize = 0;
        ImageInfo.ImageSelector = 0;
        ImageInfo.ImageSectionNumber = 0;

        /* Get the NT Headers */
        NtHeader = RtlImageNtHeader(Process->SectionBaseAddress);
        if (NtHeader)
        {
            /* Set image size */
            ImageInfo.ImageSize = NtHeader->OptionalHeader.SizeOfImage;
        }

        /* Get the image name */
        Status = MmGetFileNameForSection(Process->SectionObject, &ModuleName);
        if (NT_SUCCESS(Status))
        {
            /* Call the notify routines and free the name */
            PspRunLoadImageNotifyRoutines(&ModuleName->Name,
                                          Process->UniqueProcessId,
                                          &ImageInfo);
            ExFreePool(ModuleName);
        }
        else
        {
            /* Call the notify routines */
            PspRunLoadImageNotifyRoutines(NULL,
                                          Process->UniqueProcessId,
                                          &ImageInfo);
        }

        /* Setup the info for ntdll.dll */
        ImageInfo.Properties = 0;
        ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
        ImageInfo.ImageBase = PspSystemDllBase;
        ImageInfo.ImageSize = 0;
        ImageInfo.ImageSelector = 0;
        ImageInfo.ImageSectionNumber = 0;

        /* Get the NT Headers */
        NtHeader = RtlImageNtHeader(PspSystemDllBase);
        if (NtHeader)
        {
            /* Set image size */
            ImageInfo.ImageSize = NtHeader->OptionalHeader.SizeOfImage;
        }

        /* Call the notify routines */
        RtlInitUnicodeString(&NtDllName,
                             L"\\SystemRoot\\System32\\ntdll.dll");
        PspRunLoadImageNotifyRoutines(&NtDllName,
                                      Process->UniqueProcessId,
                                      &ImageInfo);
    }

    /* Fail if we have no port */
    DebugPort = Process->DebugPort;
    if (!DebugPort) return;

    /* Check if create was not already reported */
    if (!(ProcessFlags & PSF_CREATE_REPORTED_BIT))
    {
        /* Setup the information structure for the new thread */
        CreateProcess->InitialThread.SubSystemKey = 0;
        CreateProcess->InitialThread.StartAddress = NULL;

        /* And for the new process */
        CreateProcess->SubSystemKey = 0;
        CreateProcess->FileHandle = DbgkpSectionToFileHandle(Process->
                                                             SectionObject);
        CreateProcess->BaseOfImage = Process->SectionBaseAddress;
        CreateProcess->DebugInfoFileOffset = 0;
        CreateProcess->DebugInfoSize = 0;

        /* Get the NT Header */
        NtHeader = RtlImageNtHeader(Process->SectionBaseAddress);
        if (NtHeader)
        {
            /* Fill out data from the header */
            CreateProcess->InitialThread.StartAddress =
                (PVOID)((ULONG_PTR)NtHeader->OptionalHeader.ImageBase +
                        NtHeader->OptionalHeader.AddressOfEntryPoint);
            CreateProcess->DebugInfoFileOffset = NtHeader->FileHeader.
                                                 PointerToSymbolTable;
            CreateProcess->DebugInfoSize = NtHeader->FileHeader.
                                           NumberOfSymbols;
        }

        /* Setup the API Message */
        ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                                 (8 + sizeof(DBGKM_CREATE_PROCESS));
        ApiMessage.h.u2.ZeroInit = 0;
        ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
        ApiMessage.ApiNumber = DbgKmCreateProcessApi;

        /* Send the message */
        DbgkpSendApiMessage(&ApiMessage, FALSE);

        /* Close the handle */
        ObCloseHandle(CreateProcess->FileHandle, KernelMode);

        /* Setup the parameters */
        LoadDll->BaseOfDll = PspSystemDllBase;
        LoadDll->DebugInfoFileOffset = 0;
        LoadDll->DebugInfoSize = 0;
        LoadDll->NamePointer = NULL;

        /* Get the NT Headers */
        NtHeader = RtlImageNtHeader(PspSystemDllBase);
        if (NtHeader)
        {
            /* Fill out debug information */
            LoadDll->DebugInfoFileOffset = NtHeader->
                                           FileHeader.PointerToSymbolTable;
            LoadDll->DebugInfoSize = NtHeader->FileHeader.NumberOfSymbols;
        }

        /* Get the TEB */
        Teb = Thread->Tcb.Teb;
        if (Teb)
        {
            /* Copy the system library name and link to it */
            wcsncpy(Teb->StaticUnicodeBuffer,
                    L"ntdll.dll",
                    sizeof(Teb->StaticUnicodeBuffer) / sizeof(WCHAR));
            Teb->NtTib.ArbitraryUserPointer = Teb->StaticUnicodeBuffer;

            /* Return it in the debug event as well */
            LoadDll->NamePointer = &Teb->NtTib.ArbitraryUserPointer;
        }

        /* Get a handle */
        InitializeObjectAttributes(&ObjectAttributes,
                                   &PsNtDllPathName,
                                   OBJ_CASE_INSENSITIVE |
                                   OBJ_KERNEL_HANDLE |
                                   OBJ_FORCE_ACCESS_CHECK,
                                   NULL,
                                   NULL);
        Status = ZwOpenFile(&LoadDll->FileHandle,
                            GENERIC_READ | SYNCHRONIZE,
                            &ObjectAttributes,
                            &IoStatusBlock,
                            FILE_SHARE_DELETE |
                            FILE_SHARE_READ |
                            FILE_SHARE_WRITE,
                            FILE_SYNCHRONOUS_IO_NONALERT);
        if (NT_SUCCESS(Status))
        {
            /* Setup the API Message */
            ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                                     (8 + sizeof(DBGKM_LOAD_DLL));
            ApiMessage.h.u2.ZeroInit = 0;
            ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
            ApiMessage.ApiNumber = DbgKmLoadDllApi;

            /* Send the message */
            DbgkpSendApiMessage(&ApiMessage, TRUE);

            /* Close the handle */
            ObCloseHandle(LoadDll->FileHandle, KernelMode);
        }
    }
    else
    {
        /* Otherwise, do it just for the thread */
        CreateThread->SubSystemKey = 0;
        CreateThread->StartAddress = StartAddress;

        /* Setup the API Message */
        ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                                 (8 + sizeof(DBGKM_CREATE_THREAD));
        ApiMessage.h.u2.ZeroInit = 0;
        ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
        ApiMessage.ApiNumber = DbgKmCreateThreadApi;

        /* Send the message */
        DbgkpSendApiMessage(&ApiMessage, TRUE);
    }
}

VOID
NTAPI
DbgkExitProcess(IN NTSTATUS ExitStatus)
{
    DBGKM_MSG ApiMessage;
    PDBGKM_EXIT_PROCESS ExitProcess = &ApiMessage.ExitProcess;
    PEPROCESS Process = PsGetCurrentProcess();
    PETHREAD Thread = PsGetCurrentThread();
    PAGED_CODE();

    /* Check if this thread is hidden, doesn't have a debug port, or died */
    if ((Thread->HideFromDebugger) ||
        !(Process->DebugPort) ||
        (Thread->DeadThread))
    {
        /* Don't notify the debugger */
        return;
    }

    /* Set the exit status */
    ExitProcess->ExitStatus = ExitStatus;

    /* Setup the API Message */
    ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                             (8 + sizeof(DBGKM_EXIT_PROCESS));
    ApiMessage.h.u2.ZeroInit = 0;
    ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
    ApiMessage.ApiNumber = DbgKmExitProcessApi;

    /* Set the current exit time */
    KeQuerySystemTime(&Process->ExitTime);

    /* Send the message */
    DbgkpSendApiMessage(&ApiMessage, FALSE);
}

VOID
NTAPI
DbgkExitThread(IN NTSTATUS ExitStatus)
{
    DBGKM_MSG ApiMessage;
    PDBGKM_EXIT_THREAD ExitThread = &ApiMessage.ExitThread;
    PEPROCESS Process = PsGetCurrentProcess();
    PETHREAD Thread = PsGetCurrentThread();
    BOOLEAN Suspended;
    PAGED_CODE();

    /* Check if this thread is hidden, doesn't have a debug port, or died */
    if ((Thread->HideFromDebugger) ||
        !(Process->DebugPort) ||
        (Thread->DeadThread))
    {
        /* Don't notify the debugger */
        return;
    }

    /* Set the exit status */
    ExitThread->ExitStatus = ExitStatus;

    /* Setup the API Message */
    ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                             (8 + sizeof(DBGKM_EXIT_THREAD));
    ApiMessage.h.u2.ZeroInit = 0;
    ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
    ApiMessage.ApiNumber = DbgKmExitThreadApi;

    /* Suspend the process */
    Suspended = DbgkpSuspendProcess();

    /* Send the message */
    DbgkpSendApiMessage(&ApiMessage, FALSE);

    /* Resume the process if needed */
    if (Suspended) DbgkpResumeProcess();
}

VOID
NTAPI
DbgkMapViewOfSection(IN PVOID Section,
                     IN PVOID BaseAddress,
                     IN ULONG SectionOffset,
                     IN ULONG_PTR ViewSize)
{
    DBGKM_MSG ApiMessage;
    PDBGKM_LOAD_DLL LoadDll = &ApiMessage.LoadDll;
    PEPROCESS Process = PsGetCurrentProcess();
    PETHREAD Thread = PsGetCurrentThread();
    PIMAGE_NT_HEADERS NtHeader;
    PAGED_CODE();
    DBGKTRACE(DBGK_PROCESS_DEBUG,
              "Section: %p. Base: %p\n", Section, BaseAddress);

    /* Check if this thread is kernel, hidden or doesn't have a debug port */
    if ((ExGetPreviousMode() == KernelMode) ||
        (Thread->HideFromDebugger) ||
        !(Process->DebugPort))
    {
        /* Don't notify the debugger */
        return;
    }

    /* Setup the parameters */
    LoadDll->FileHandle = DbgkpSectionToFileHandle(Section);
    LoadDll->BaseOfDll = BaseAddress;
    LoadDll->DebugInfoFileOffset = 0;
    LoadDll->DebugInfoSize = 0;

    /* Get the NT Headers */
    NtHeader = RtlImageNtHeader(BaseAddress);
    if (NtHeader)
    {
        /* Fill out debug information */
        LoadDll->DebugInfoFileOffset = NtHeader->FileHeader.
                                       PointerToSymbolTable;
        LoadDll->DebugInfoSize = NtHeader->FileHeader.NumberOfSymbols;
    }

    /* Setup the API Message */
    ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                             (8 + sizeof(DBGKM_LOAD_DLL));
    ApiMessage.h.u2.ZeroInit = 0;
    ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
    ApiMessage.ApiNumber = DbgKmLoadDllApi;

    /* Send the message */
    DbgkpSendApiMessage(&ApiMessage, TRUE);

    /* Close the handle */
    ObCloseHandle(LoadDll->FileHandle, KernelMode);
}

VOID
NTAPI
DbgkUnMapViewOfSection(IN PVOID BaseAddress)
{
    DBGKM_MSG ApiMessage;
    PDBGKM_UNLOAD_DLL UnloadDll = &ApiMessage.UnloadDll;
    PEPROCESS Process = PsGetCurrentProcess();
    PETHREAD Thread = PsGetCurrentThread();
    PAGED_CODE();

    /* Check if this thread is kernel, hidden or doesn't have a debug port */
    if ((ExGetPreviousMode() == KernelMode) ||
        (Thread->HideFromDebugger) ||
        !(Process->DebugPort))
    {
        /* Don't notify the debugger */
        return;
    }

    /* Set the DLL Base */
    UnloadDll->BaseAddress = BaseAddress;

    /* Setup the API Message */
    ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                             (8 + sizeof(DBGKM_UNLOAD_DLL));
    ApiMessage.h.u2.ZeroInit = 0;
    ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
    ApiMessage.ApiNumber = DbgKmUnloadDllApi;

    /* Send the message */
    DbgkpSendApiMessage(&ApiMessage, TRUE);
}
