//----------------------------------------------------------------------------
//
// IDebugSystemObjects implementations.
//
// Copyright (C) Microsoft Corporation, 1999-2001.
//
//----------------------------------------------------------------------------

#include "ntsdp.hpp"

ULONG64 g_ImplicitThreadData;
BOOL    g_ImplicitThreadDataIsDefault = TRUE;
ULONG64 g_ImplicitProcessData;
BOOL    g_ImplicitProcessDataIsDefault = TRUE;

//----------------------------------------------------------------------------
//
// Utility functions.
//
//----------------------------------------------------------------------------

HRESULT
EmulateNtSelDescriptor(MachineInfo* Machine,
                       ULONG Selector, PDESCRIPTOR64 Desc)
{
    // Only emulate on platforms that support segments.
    if (Machine->m_ExecTypes[0] != IMAGE_FILE_MACHINE_I386 &&
        Machine->m_ExecTypes[0] != IMAGE_FILE_MACHINE_AMD64)
    {
        return E_UNEXPECTED;
    }

    ULONG Type, Gran;

    //
    // For user mode and triage dumps, we can hardcode the selector
    // since we do not have it anywhere.
    // XXX drewb - How many should we fake?  There are quite
    // a few KGDT entries.  Which ones are valid in user mode and
    // which are valid for a triage dump?
    //

    switch(Selector)
    {
    case X86_KGDT_R3_TEB + 3:
        HRESULT Status;

        // In user mode fs points to the TEB so fake up
        // a selector for it.
        if ((Status = GetImplicitThreadDataTeb(&Desc->Base)) != S_OK)
        {
            return Status;
        }

        if (Machine != g_TargetMachine)
        {
            ULONG Read;

            // We're asking for an emulated machine's TEB.
            // The only case we currently handle is x86-on-IA64
            // for Wow64, where the 32-bit TEB pointer is
            // stored in NT_TIB.ExceptionList.
            // Conveniently, this is the very first entry.
            if ((Status = g_Target->ReadVirtual(Desc->Base, &Desc->Base,
                                                sizeof(ULONG), &Read)) != S_OK)
            {
                return Status;
            }
            if (Read != sizeof(ULONG))
            {
                return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
            }

            Desc->Base = EXTEND64(Desc->Base);
        }

        Desc->Limit = Machine->m_PageSize - 1;
        Type = 0x13;
        Gran = 0;
        break;

    case X86_KGDT_R3_DATA + 3:
        Desc->Base = 0;
        Desc->Limit = Machine->m_Ptr64 ? 0xffffffffffffffffI64 : 0xffffffff;
        Type = 0x13;
        Gran = X86_DESC_GRANULARITY;
        break;

    default:
        Desc->Base = 0;
        Desc->Limit = Machine->m_Ptr64 ? 0xffffffffffffffffI64 : 0xffffffff;
        Type = 0x1b;
        Gran = X86_DESC_GRANULARITY |
            (Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_AMD64 ?
             X86_DESC_LONG_MODE : 0);
        break;
    }

    Desc->Flags = Gran | X86_DESC_DEFAULT_BIG | X86_DESC_PRESENT | Type |
        (IS_USER_TARGET() ?
         (3 << X86_DESC_PRIVILEGE_SHIFT) : (Selector & 3));

    return S_OK;
}

void
X86DescriptorToDescriptor64(PX86_LDT_ENTRY X86Desc,
                            PDESCRIPTOR64 Desc64)
{
    Desc64->Base = EXTEND64((ULONG)X86Desc->BaseLow +
                            ((ULONG)X86Desc->HighWord.Bits.BaseMid << 16) +
                            ((ULONG)X86Desc->HighWord.Bytes.BaseHi << 24));
    Desc64->Limit = (ULONG)X86Desc->LimitLow +
        ((ULONG)X86Desc->HighWord.Bits.LimitHi << 16);
    if (X86Desc->HighWord.Bits.Granularity)
    {
        Desc64->Limit = (Desc64->Limit << X86_PAGE_SHIFT) +
            ((1 << X86_PAGE_SHIFT) - 1);
    }
    Desc64->Flags = (ULONG)X86Desc->HighWord.Bytes.Flags1 +
        (((ULONG)X86Desc->HighWord.Bytes.Flags2 >> 4) << 8);
}

HRESULT
ReadX86Descriptor(ULONG Selector, ULONG64 Base, ULONG Limit,
                  PDESCRIPTOR64 Desc)
{
    ULONG Index;

    // Mask off irrelevant bits
    Index = Selector & ~0x7;

    // Check to make sure that the selector is within the table bounds
    if (Index > Limit)
    {
        return E_INVALIDARG;
    }

    HRESULT Status;
    X86_LDT_ENTRY X86Desc;
    ULONG Result;

    Status = g_Target->ReadVirtual(Base + Index, &X86Desc, sizeof(X86Desc),
                                   &Result);
    if (Status != S_OK)
    {
        return Status;
    }
    if (Result != sizeof(X86Desc))
    {
        return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
    }

    X86DescriptorToDescriptor64(&X86Desc, Desc);
    return S_OK;
}

//----------------------------------------------------------------------------
//
// TargetInfo system object methods.
//
//----------------------------------------------------------------------------

HRESULT
TargetInfo::GetProcessorSystemDataOffset(
    IN ULONG Processor,
    IN ULONG Index,
    OUT PULONG64 Offset
    )
{
    if (!IS_KERNEL_TARGET())
    {
        return E_UNEXPECTED;
    }

    // XXX drewb - Temporary until different OS support is
    // sorted out.
    if (g_ActualSystemVersion < NT_SVER_START ||
        g_ActualSystemVersion > NT_SVER_END)
    {
        return E_UNEXPECTED;
    }

    HRESULT Status;
    ULONG Read;

    if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386)
    {
        DESCRIPTOR64 Entry;

        //
        // We always need the PCR address so go ahead and get it.
        //
        
        if (!IS_CONTEXT_ACCESSIBLE())
        {
            X86_DESCRIPTOR GdtDesc;
            
            // We can't go through the normal context segreg mapping
            // but all we really need is an entry from the
            // kernel GDT that should always be present and
            // constant while the system is initialized.  We
            // can get the GDT information from the x86 control
            // space so do that.
            if ((Status = ReadControl
                 (Processor, g_TargetMachine->m_OffsetSpecialRegisters +
                  FIELD_OFFSET(X86_KSPECIAL_REGISTERS, Gdtr),
                  &GdtDesc, sizeof(GdtDesc), &Read)) != S_OK ||
                Read != sizeof(GdtDesc) ||
                (Status = ReadX86Descriptor(X86_KGDT_R0_PCR,
                                            EXTEND64(GdtDesc.Base),
                                            GdtDesc.Limit, &Entry)) != S_OK)
            {
                ErrOut("Unable to read selector for PCR for processor %u\n",
                       Processor);
                return Status != S_OK ?
                    Status : HRESULT_FROM_WIN32(ERROR_READ_FAULT);
            }
        }
        else
        {
            if ((Status = GetSelDescriptor(g_TargetMachine,
                                           VIRTUAL_THREAD_HANDLE(Processor),
                                           X86_KGDT_R0_PCR, &Entry)) != S_OK)
            {
                ErrOut("Unable to read selector for PCR for processor %u\n",
                       Processor);
                return Status;
            }
        }

        switch(Index)
        {
        case DEBUG_DATA_KPCR_OFFSET:

            Status = ReadPointer(g_TargetMachine,
                                 Entry.Base + FIELD_OFFSET(X86_KPCR, SelfPcr),
                                 Offset);
            if ((Status != S_OK) || Entry.Base != *Offset)
            {
                ErrOut("KPCR is corrupted !");
            }

            *Offset = Entry.Base;
            break;

        case DEBUG_DATA_KPRCB_OFFSET:
        case DEBUG_DATA_KTHREAD_OFFSET:
            Status = ReadPointer(g_TargetMachine,
                                 Entry.Base + FIELD_OFFSET(X86_KPCR, Prcb),
                                 Offset);
            if (Status != S_OK)
            {
                return Status;
            }

            if (Index == DEBUG_DATA_KPRCB_OFFSET)
            {
                break;
            }

            Status = ReadPointer(g_TargetMachine,
                                 *Offset + KPRCB_CURRENT_THREAD_OFFSET_32,
                                 Offset);
            if (Status != S_OK)
            {
                return Status;
            }
            break;
        }
    }
    else
    {
        ULONG ReadSize = g_TargetMachine->m_Ptr64 ?
            sizeof(ULONG64) : sizeof(ULONG);
        ULONG64 Address;

        switch(g_TargetMachineType)
        {
        case IMAGE_FILE_MACHINE_ALPHA:
        case IMAGE_FILE_MACHINE_AXP64:
            switch(Index)
            {
            case DEBUG_DATA_KPCR_OFFSET:
                Index = ALPHA_DEBUG_CONTROL_SPACE_PCR;
                break;
            case DEBUG_DATA_KPRCB_OFFSET:
                Index = ALPHA_DEBUG_CONTROL_SPACE_PRCB;
                break;
            case DEBUG_DATA_KTHREAD_OFFSET:
                Index = ALPHA_DEBUG_CONTROL_SPACE_THREAD;
                break;
            }
            break;

        case IMAGE_FILE_MACHINE_IA64:
            switch(Index)
            {
            case DEBUG_DATA_KPCR_OFFSET:
                Index = IA64_DEBUG_CONTROL_SPACE_PCR;
                break;
            case DEBUG_DATA_KPRCB_OFFSET:
                Index = IA64_DEBUG_CONTROL_SPACE_PRCB;
                break;
            case DEBUG_DATA_KTHREAD_OFFSET:
                Index = IA64_DEBUG_CONTROL_SPACE_THREAD;
                break;
            }
            break;
            
        case IMAGE_FILE_MACHINE_AMD64:
            switch(Index)
            {
            case DEBUG_DATA_KPCR_OFFSET:
                Index = AMD64_DEBUG_CONTROL_SPACE_PCR;
                break;
            case DEBUG_DATA_KPRCB_OFFSET:
                Index = AMD64_DEBUG_CONTROL_SPACE_PRCB;
                break;
            case DEBUG_DATA_KTHREAD_OFFSET:
                Index = AMD64_DEBUG_CONTROL_SPACE_THREAD;
                break;
            }
            break;
        }

        Status = ReadControl(Processor, Index, &Address, ReadSize, &Read);
        if (Status != S_OK)
        {
            return Status;
        }
        else if (Read != ReadSize)
        {
            return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
        }
        if (!g_TargetMachine->m_Ptr64)
        {
            Address = EXTEND64(Address);
        }

        *Offset = Address;
    }

    return S_OK;
}

HRESULT
TargetInfo::GetTargetSegRegDescriptors(ULONG64 Thread,
                                       ULONG Start, ULONG Count,
                                       PDESCRIPTOR64 Descs)
{
    while (Count-- > 0)
    {
        Descs->Flags = SEGDESC_INVALID;
        Descs++;
    }

    return S_OK;
}

HRESULT
TargetInfo::GetTargetSpecialRegisters
    (ULONG64 Thread, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special)
{
    HRESULT Status;
    ULONG Done;

    Status = ReadControl(VIRTUAL_THREAD_INDEX(Thread),
                         g_TargetMachine->m_OffsetSpecialRegisters,
                         Special, g_TargetMachine->m_SizeKspecialRegisters,
                         &Done);
    if (Status != S_OK)
    {
        return Status;
    }
    return Done == g_TargetMachine->m_SizeKspecialRegisters ? S_OK : E_FAIL;
}

HRESULT
TargetInfo::SetTargetSpecialRegisters
    (ULONG64 Thread, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special)
{
    HRESULT Status;
    ULONG Done;

    Status = WriteControl(VIRTUAL_THREAD_INDEX(Thread),
                          g_TargetMachine->m_OffsetSpecialRegisters,
                          Special, g_TargetMachine->m_SizeKspecialRegisters,
                          &Done);
    if (Status != S_OK)
    {
        return Status;
    }
    return Done == g_TargetMachine->m_SizeKspecialRegisters ? S_OK : E_FAIL;
}

void
TargetInfo::InvalidateTargetContext(void)
{
    // Nothing to do.
}

HRESULT
TargetInfo::GetContext(
    ULONG64 Thread,
    PCROSS_PLATFORM_CONTEXT Context
    )
{
    if (g_TargetMachine == NULL)
    {
        return E_UNEXPECTED;
    }

    HRESULT Status;
    CROSS_PLATFORM_CONTEXT TargetContextBuffer;
    PCROSS_PLATFORM_CONTEXT TargetContext;

    if (g_TargetMachine->m_SverCanonicalContext <= g_SystemVersion)
    {
        TargetContext = Context;
    }
    else
    {
        TargetContext = &TargetContextBuffer;
        g_TargetMachine->InitializeContextFlags(TargetContext,
                                                g_SystemVersion);
    }

    Status = GetTargetContext(Thread, TargetContext);

    if (Status == S_OK && TargetContext == &TargetContextBuffer)
    {
        Status = g_TargetMachine->
            ConvertContextFrom(Context, g_SystemVersion,
                               g_TargetMachine->m_SizeTargetContext,
                               TargetContext);
        // Conversion should always succeed since the size is
        // known to be valid.
        DBG_ASSERT(Status == S_OK);
    }

    return Status;
}

HRESULT
TargetInfo::SetContext(
    ULONG64 Thread,
    PCROSS_PLATFORM_CONTEXT Context
    )
{
    if (g_TargetMachine == NULL)
    {
        return E_UNEXPECTED;
    }

    CROSS_PLATFORM_CONTEXT TargetContextBuffer;
    PCROSS_PLATFORM_CONTEXT TargetContext;

    if (g_TargetMachine->m_SverCanonicalContext <= g_SystemVersion)
    {
        TargetContext = Context;
    }
    else
    {
        TargetContext = &TargetContextBuffer;
        HRESULT Status = g_TargetMachine->
            ConvertContextTo(Context, g_SystemVersion,
                             g_TargetMachine->m_SizeTargetContext,
                             TargetContext);
        // Conversion should always succeed since the size is
        // known to be valid.
        DBG_ASSERT(Status == S_OK);
    }

    return SetTargetContext(Thread, TargetContext);
}

HRESULT
TargetInfo::KdGetThreadInfoDataOffset(PTHREAD_INFO Thread,
                                      ULONG64 ThreadHandle,
                                      PULONG64 Offset)
{
    if (Thread != NULL && Thread->DataOffset != 0)
    {
        *Offset = Thread->DataOffset;
        return S_OK;
    }

    ULONG Processor;

    if (Thread != NULL)
    {
        ThreadHandle = Thread->Handle;
    }
    Processor = VIRTUAL_THREAD_INDEX(ThreadHandle);

    HRESULT Status;

    Status = GetProcessorSystemDataOffset(Processor,
                                          DEBUG_DATA_KTHREAD_OFFSET,
                                          Offset);

    if (Status == S_OK && Thread != NULL)
    {
        Thread->DataOffset = *Offset;
    }

    return Status;
}

HRESULT
TargetInfo::KdGetProcessInfoDataOffset(PTHREAD_INFO Thread,
                                       ULONG Processor,
                                       ULONG64 ThreadData,
                                       PULONG64 Offset)
{
    // Process data offsets are not cached for kernel mode
    // since the only PROCESS_INFO is for kernel space.

    ULONG64 ProcessAddr;
    HRESULT Status;

    if (ThreadData == 0)
    {
        Status = GetThreadInfoDataOffset(Thread,
                                         VIRTUAL_THREAD_HANDLE(Processor),
                                         &ThreadData);
        if (Status != S_OK)
        {
            return Status;
        }
    }

    ThreadData += g_TargetMachine->m_OffsetKThreadApcProcess;

    Status = ReadPointer(g_TargetMachine, ThreadData, &ProcessAddr);
    if (Status != S_OK)
    {
        ErrOut("Unable to read KTHREAD address %p\n", ThreadData);
    }
    else
    {
        *Offset = ProcessAddr;
    }

    return Status;
}

HRESULT
TargetInfo::KdGetThreadInfoTeb(PTHREAD_INFO Thread,
                               ULONG ThreadIndex,
                               ULONG64 ThreadData,
                               PULONG64 Offset)
{
    ULONG64 TebAddr;
    HRESULT Status;

    if (ThreadData == 0)
    {
        Status = GetThreadInfoDataOffset(Thread,
                                         VIRTUAL_THREAD_HANDLE(ThreadIndex),
                                         &ThreadData);
        if (Status != S_OK)
        {
            return Status;
        }
    }

    ThreadData += g_TargetMachine->m_OffsetKThreadTeb;

    Status = ReadPointer(g_TargetMachine, ThreadData, &TebAddr);
    if (Status != S_OK)
    {
        ErrOut("Could not read KTHREAD address %p\n", ThreadData);
    }
    else
    {
        *Offset = TebAddr;
    }

    return Status;
}

HRESULT
TargetInfo::KdGetProcessInfoPeb(PTHREAD_INFO Thread,
                                ULONG Processor,
                                ULONG64 ThreadData,
                                PULONG64 Offset)
{
    HRESULT Status;
    ULONG64 Process, PebAddr;

    Status = GetProcessInfoDataOffset(Thread, Processor,
                                      ThreadData, &Process);
    if (Status != S_OK)
    {
        return Status;
    }

    Process += g_TargetMachine->m_OffsetEprocessPeb;

    Status = ReadPointer(g_TargetMachine, Process, &PebAddr);
    if (Status != S_OK)
    {
        ErrOut("Could not read KPROCESS address %p\n", Process);
    }
    else
    {
        *Offset = PebAddr;
    }

    return Status;
}

#define SELECTOR_CACHE_LENGTH 6

typedef struct _sc
{
    struct _sc            *nextYoungest;
    struct _sc            *nextOldest;
    ULONG                  Processor;
    ULONG                  Selector;
    DESCRIPTOR64           Desc;
} SELCACHEENTRY;

SELCACHEENTRY SelectorCache[SELECTOR_CACHE_LENGTH], *selYoungest, *selOldest;

void
InitSelCache(void)
{
    int i;

    for (i = 0; i < SELECTOR_CACHE_LENGTH; i++)
    {
        SelectorCache[i].nextYoungest = &SelectorCache[i+1];
        SelectorCache[i].nextOldest   = &SelectorCache[i-1];
        SelectorCache[i].Processor    = (LONG) -1;
        SelectorCache[i].Selector     = 0;
    }
    SelectorCache[--i].nextYoungest = NULL;
    SelectorCache[0].nextOldest     = NULL;
    selYoungest = &SelectorCache[i];
    selOldest   = &SelectorCache[0];
}

BOOL
FindSelector(ULONG Processor, ULONG Selector, PDESCRIPTOR64 Desc)
{
    int i;

    for (i = 0; i < SELECTOR_CACHE_LENGTH; i++)
    {
        if (SelectorCache[i].Selector == Selector &&
            SelectorCache[i].Processor == Processor)
        {
            *Desc = SelectorCache[i].Desc;
            return TRUE;
        }
    }
    return FALSE;
}

void
PutSelector(ULONG Processor, ULONG Selector, PDESCRIPTOR64 Desc)
{
    selOldest->Processor = Processor;
    selOldest->Selector = Selector;
    selOldest->Desc = *Desc;
    (selOldest->nextYoungest)->nextOldest = NULL;
    selOldest->nextOldest = selYoungest;
    selYoungest->nextYoungest = selOldest;
    selYoungest = selOldest;
    selOldest = selOldest->nextYoungest;
}

HRESULT
TargetInfo::KdGetSelDescriptor(MachineInfo* Machine,
                               ULONG64 Thread, ULONG Selector,
                               PDESCRIPTOR64 Desc)
{
    ULONG Processor = VIRTUAL_THREAD_INDEX(Thread);

    if (FindSelector(Processor, Selector, Desc))
    {
        return S_OK;
    }

    PTHREAD_INFO CtxThread, ProcThread;

    ProcThread = FindThreadByHandle(g_CurrentProcess, Thread);
    if (ProcThread == NULL)
    {
        return E_INVALIDARG;
    }

    CtxThread = g_RegContextThread;
    ChangeRegContext(ProcThread);
    
    ULONG TableReg;
    DESCRIPTOR64 Table;
    HRESULT Status;

    // Find out whether this is a GDT or LDT selector
    if (Selector & 0x4)
    {
        TableReg = SEGREG_LDT;
    }
    else
    {
        TableReg = SEGREG_GDT;
    }

    //
    // Fetch the address and limit of the appropriate descriptor table,
    // then look up the selector entry.
    //

    if ((Status = Machine->GetSegRegDescriptor(TableReg, &Table)) != S_OK)
    {
        goto Exit;
    }

    Status = ReadX86Descriptor(Selector, Table.Base, (ULONG)Table.Limit, Desc);
    if (Status == S_OK)
    {
        PutSelector(Processor, Selector, Desc);
    }

 Exit:
    ChangeRegContext(CtxThread);
    return Status;
}

//----------------------------------------------------------------------------
//
// LiveKernelTargetInfo system object methods.
//
//----------------------------------------------------------------------------

HRESULT
LiveKernelTargetInfo::GetThreadIdByProcessor(
    IN ULONG Processor,
    OUT PULONG Id
    )
{
    *Id = VIRTUAL_THREAD_ID(Processor);
    return S_OK;
}

HRESULT
LiveKernelTargetInfo::GetThreadInfoDataOffset(PTHREAD_INFO Thread,
                                          ULONG64 ThreadHandle,
                                          PULONG64 Offset)
{
    return KdGetThreadInfoDataOffset(Thread, ThreadHandle, Offset);
}

HRESULT
LiveKernelTargetInfo::GetProcessInfoDataOffset(PTHREAD_INFO Thread,
                                           ULONG Processor,
                                           ULONG64 ThreadData,
                                           PULONG64 Offset)
{
    return KdGetProcessInfoDataOffset(Thread, Processor, ThreadData, Offset);
}

HRESULT
LiveKernelTargetInfo::GetThreadInfoTeb(PTHREAD_INFO Thread,
                                   ULONG ThreadIndex,
                                   ULONG64 ThreadData,
                                   PULONG64 Offset)
{
    return KdGetThreadInfoTeb(Thread, ThreadIndex, ThreadData, Offset);
}

HRESULT
LiveKernelTargetInfo::GetProcessInfoPeb(PTHREAD_INFO Thread,
                                    ULONG Processor,
                                    ULONG64 ThreadData,
                                    PULONG64 Offset)
{
    return KdGetProcessInfoPeb(Thread, Processor, ThreadData, Offset);
}

HRESULT
LiveKernelTargetInfo::GetSelDescriptor(MachineInfo* Machine,
                                       ULONG64 Thread, ULONG Selector,
                                       PDESCRIPTOR64 Desc)
{
    return KdGetSelDescriptor(Machine, Thread, Selector, Desc);
}

//----------------------------------------------------------------------------
//
// ConnLiveKernelTargetInfo system object methods.
//
//----------------------------------------------------------------------------

HRESULT
ConnLiveKernelTargetInfo::GetTargetContext(
    ULONG64 Thread,
    PVOID Context
    )
{
    NTSTATUS Status = DbgKdGetContext((USHORT)VIRTUAL_THREAD_INDEX(Thread),
                                      (PCROSS_PLATFORM_CONTEXT)Context);
    return CONV_NT_STATUS(Status);
}

HRESULT
ConnLiveKernelTargetInfo::SetTargetContext(
    ULONG64 Thread,
    PVOID Context
    )
{
    NTSTATUS Status = DbgKdSetContext((USHORT)VIRTUAL_THREAD_INDEX(Thread),
                                      (PCROSS_PLATFORM_CONTEXT)Context);
    return CONV_NT_STATUS(Status);
}

//----------------------------------------------------------------------------
//
// LocalLiveKernelTargetInfo system object methods.
//
//----------------------------------------------------------------------------

HRESULT
LocalLiveKernelTargetInfo::GetTargetContext(
    ULONG64 Thread,
    PVOID Context
    )
{
    // There really isn't any way to make
    // this work in a meaningful way unless the system
    // is paused.
    return E_NOTIMPL;
}

HRESULT
LocalLiveKernelTargetInfo::SetTargetContext(
    ULONG64 Thread,
    PVOID Context
    )
{
    // There really isn't any way to make
    // this work in a meaningful way unless the system
    // is paused.
    return E_NOTIMPL;
}

//----------------------------------------------------------------------------
//
// ExdiLiveKernelTargetInfo system object methods.
//
//----------------------------------------------------------------------------

HRESULT
ExdiLiveKernelTargetInfo::GetProcessorSystemDataOffset(
    IN ULONG Processor,
    IN ULONG Index,
    OUT PULONG64 Offset
    )
{
    if (m_KdSupport != EXDI_KD_GS_PCR ||
        g_TargetMachineType != IMAGE_FILE_MACHINE_AMD64)
    {
        return LiveKernelTargetInfo::
            GetProcessorSystemDataOffset(Processor, Index, Offset);
    }

    HRESULT Status;
    DESCRIPTOR64 GsDesc;
        
    if ((Status =
         GetTargetSegRegDescriptors(0, SEGREG_GS, 1, &GsDesc)) != S_OK)
    {
        return Status;
    }

    switch(Index)
    {
    case DEBUG_DATA_KPCR_OFFSET:
        *Offset = GsDesc.Base;
        break;

    case DEBUG_DATA_KPRCB_OFFSET:
    case DEBUG_DATA_KTHREAD_OFFSET:
        Status = ReadPointer(g_TargetMachine,
                             GsDesc.Base +
                             FIELD_OFFSET(AMD64_KPCR, CurrentPrcb),
                             Offset);
        if (Status != S_OK)
        {
            return Status;
        }

        if (Index == DEBUG_DATA_KPRCB_OFFSET)
        {
            break;
        }

        Status = ReadPointer(g_TargetMachine,
                             *Offset + KPRCB_CURRENT_THREAD_OFFSET_64,
                             Offset);
        if (Status != S_OK)
        {
            return Status;
        }
        break;
    }

    return S_OK;
}

HRESULT
ExdiLiveKernelTargetInfo::GetTargetContext(
    ULONG64 Thread,
    PVOID Context
    )
{
    if (!m_ContextValid)
    {
        HRESULT Status;

        if ((Status = g_TargetMachine->
             GetExdiContext(m_Context, &m_ContextData)) != S_OK)
        {
            return Status;
        }

        m_ContextValid = TRUE;
    }

    g_TargetMachine->ConvertExdiContextToContext
        (&m_ContextData, (PCROSS_PLATFORM_CONTEXT)Context);
    return S_OK;
}

HRESULT
ExdiLiveKernelTargetInfo::SetTargetContext(
    ULONG64 Thread,
    PVOID Context
    )
{
    if (!m_ContextValid)
    {
        HRESULT Status;

        if ((Status = g_TargetMachine->
             GetExdiContext(m_Context, &m_ContextData)) != S_OK)
        {
            return Status;
        }

        m_ContextValid = TRUE;
    }

    g_TargetMachine->ConvertExdiContextFromContext
        ((PCROSS_PLATFORM_CONTEXT)Context, &m_ContextData);
    return g_TargetMachine->SetExdiContext(m_Context, &m_ContextData);
}

HRESULT
ExdiLiveKernelTargetInfo::GetTargetSegRegDescriptors(ULONG64 Thread,
                                                     ULONG Start, ULONG Count,
                                                     PDESCRIPTOR64 Descs)
{
    if (!m_ContextValid)
    {
        HRESULT Status;

        if ((Status = g_TargetMachine->
             GetExdiContext(m_Context, &m_ContextData)) != S_OK)
        {
            return Status;
        }

        m_ContextValid = TRUE;
    }

    g_TargetMachine->ConvertExdiContextToSegDescs(&m_ContextData, Start,
                                                  Count, Descs);
    return S_OK;
}

void
ExdiLiveKernelTargetInfo::InvalidateTargetContext(void)
{
    m_ContextValid = FALSE;
}

HRESULT
ExdiLiveKernelTargetInfo::GetTargetSpecialRegisters
    (ULONG64 Thread, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special)
{
    if (!m_ContextValid)
    {
        HRESULT Status;

        if ((Status = g_TargetMachine->
             GetExdiContext(m_Context, &m_ContextData)) != S_OK)
        {
            return Status;
        }

        m_ContextValid = TRUE;
    }

    g_TargetMachine->ConvertExdiContextToSpecial(&m_ContextData, Special);
    return S_OK;
}

HRESULT
ExdiLiveKernelTargetInfo::SetTargetSpecialRegisters
    (ULONG64 Thread, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special)
{
    if (!m_ContextValid)
    {
        HRESULT Status;

        if ((Status = g_TargetMachine->
             GetExdiContext(m_Context, &m_ContextData)) != S_OK)
        {
            return Status;
        }

        m_ContextValid = TRUE;
    }

    g_TargetMachine->ConvertExdiContextFromSpecial(Special, &m_ContextData);
    return g_TargetMachine->SetExdiContext(m_Context, &m_ContextData);
}

//----------------------------------------------------------------------------
//
// UserTargetInfo system object methods.
//
//----------------------------------------------------------------------------

HRESULT
UserTargetInfo::GetTargetContext(
    ULONG64 Thread,
    PVOID Context
    )
{
    return m_Services->
        // g_TargetMachine should be used because of Wx86 debugging
        GetContext(Thread,
                   *(PULONG)((PUCHAR)Context +
                             g_TargetMachine->m_OffsetTargetContextFlags),
                   g_TargetMachine->m_OffsetTargetContextFlags,
                   Context,
                   g_TargetMachine->m_SizeTargetContext, NULL);
}

HRESULT
UserTargetInfo::SetTargetContext(
    ULONG64 Thread,
    PVOID Context
    )
{
    return m_Services->SetContext(Thread, Context,
                                  // g_TargetMachine should be used because of Wx86 debugging
                                  g_TargetMachine->m_SizeTargetContext, NULL);
}

HRESULT
UserTargetInfo::GetThreadInfoDataOffset(PTHREAD_INFO Thread,
                                        ULONG64 ThreadHandle,
                                        PULONG64 Offset)
{
    if (Thread != NULL && Thread->DataOffset != 0)
    {
        *Offset = Thread->DataOffset;
        return S_OK;
    }

    if (Thread != NULL)
    {
        ThreadHandle = Thread->Handle;
    }
    else if (ThreadHandle == NULL)
    {
        ThreadHandle = g_CurrentProcess->CurrentThread->Handle;
    }

    HRESULT Status = m_Services->
        GetThreadDataOffset(ThreadHandle, Offset);

    if (Status == S_OK)
    {
        if (Thread != NULL)
        {
            Thread->DataOffset = *Offset;
        }
    }

    return Status;
}

HRESULT
UserTargetInfo::GetProcessInfoDataOffset(PTHREAD_INFO Thread,
                                         ULONG Processor,
                                         ULONG64 ThreadData,
                                         PULONG64 Offset)
{
    HRESULT Status;
    PPROCESS_INFO Process;

    // Processor isn't any use so default to the current thread.
    if (Thread == NULL)
    {
        Process = g_CurrentProcess;
    }
    else
    {
        Process = Thread->Process;
    }

    if (ThreadData == 0 && Process->DataOffset != 0)
    {
        *Offset = Process->DataOffset;
        return S_OK;
    }

    if (ThreadData != 0)
    {
        if (g_DebuggerPlatformId == VER_PLATFORM_WIN32_NT)
        {
            ThreadData += g_TargetMachine->m_Ptr64 ?
                PEB_FROM_TEB64 : PEB_FROM_TEB32;
            Status = ReadPointer(g_TargetMachine, ThreadData, Offset);
        }
        else
        {
            Status = E_NOTIMPL;
        }
    }
    else
    {
        Status = m_Services->GetProcessDataOffset(Process->FullHandle, Offset);
    }

    if (Status == S_OK)
    {
        if (ThreadData == 0)
        {
            Process->DataOffset = *Offset;
        }
    }

    return Status;
}

HRESULT
UserTargetInfo::GetThreadInfoTeb(PTHREAD_INFO Thread,
                                 ULONG Processor,
                                 ULONG64 ThreadData,
                                 PULONG64 Offset)
{
    return GetThreadInfoDataOffset(Thread, ThreadData, Offset);
}

HRESULT
UserTargetInfo::GetProcessInfoPeb(PTHREAD_INFO Thread,
                                  ULONG Processor,
                                  ULONG64 ThreadData,
                                  PULONG64 Offset)
{
    // Thread data is not useful.
    return GetProcessInfoDataOffset(Thread, 0, 0, Offset);
}

HRESULT
UserTargetInfo::GetSelDescriptor(MachineInfo* Machine,
                                 ULONG64 Thread, ULONG Selector,
                                 PDESCRIPTOR64 Desc)
{
    HRESULT Status;
    ULONG Used;
    X86_LDT_ENTRY X86Desc;

    if ((Status = m_Services->
         DescribeSelector(Thread, Selector, &X86Desc, sizeof(X86Desc),
                          &Used)) != S_OK)
    {
        return Status;
    }
    if (Used != sizeof(X86Desc))
    {
        return E_FAIL;
    }

    X86DescriptorToDescriptor64(&X86Desc, Desc);
    return S_OK;
}

//----------------------------------------------------------------------------
//
// IDebugSystemObjects methods.
//
//----------------------------------------------------------------------------

STDMETHODIMP
DebugClient::GetEventThread(
    THIS_
    OUT PULONG Id
    )
{
    ENTER_ENGINE();

    HRESULT Status;

    if (g_EventThread == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        *Id = g_EventThread->UserId;
        Status = S_OK;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetEventProcess(
    THIS_
    OUT PULONG Id
    )
{
    ENTER_ENGINE();

    HRESULT Status;

    if (g_EventProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        *Id = g_EventProcess->UserId;
        Status = S_OK;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentThreadId(
    THIS_
    OUT PULONG Id
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL ||
        g_CurrentProcess->CurrentThread == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        *Id = g_CurrentProcess->CurrentThread->UserId;
        Status = S_OK;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::SetCurrentThreadId(
    THIS_
    IN ULONG Id
    )
{
    ENTER_ENGINE();

    HRESULT Status;
    PTHREAD_INFO Thread = FindThreadByUserId(NULL, Id);
    if (Thread != NULL)
    {
        SetCurrentThread(Thread, FALSE);
        ResetCurrentScopeLazy();
        Status = S_OK;
    }
    else
    {
        Status = E_NOINTERFACE;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentProcessId(
    THIS_
    OUT PULONG Id
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        *Id = g_CurrentProcess->UserId;
        Status = S_OK;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::SetCurrentProcessId(
    THIS_
    IN ULONG Id
    )
{
    ENTER_ENGINE();

    HRESULT Status;
    PPROCESS_INFO Process = FindProcessByUserId(Id);
    if (Process != NULL)
    {
        if (Process->CurrentThread == NULL)
        {
            Process->CurrentThread = Process->ThreadHead;
        }
        if (Process->CurrentThread == NULL)
        {
            Status = E_FAIL;
        }
        else
        {
            SetCurrentThread(Process->CurrentThread, FALSE);
            ResetCurrentScopeLazy();
            Status = S_OK;
        }
    }
    else
    {
        Status = E_NOINTERFACE;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetNumberThreads(
    THIS_
    OUT PULONG Number
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        *Number = g_CurrentProcess->NumberThreads;
        Status = S_OK;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetTotalNumberThreads(
    THIS_
    OUT PULONG Total,
    OUT PULONG LargestProcess
    )
{
    ENTER_ENGINE();

    *Total = g_TotalNumberThreads;
    *LargestProcess = g_MaxThreadsInProcess;

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
DebugClient::GetThreadIdsByIndex(
    THIS_
    IN ULONG Start,
    IN ULONG Count,
    OUT OPTIONAL /* size_is(Count) */ PULONG Ids,
    OUT OPTIONAL /* size_is(Count) */ PULONG SysIds
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        PTHREAD_INFO Thread;
        ULONG Index;

        if (Start >= g_CurrentProcess->NumberThreads ||
            Start + Count > g_CurrentProcess->NumberThreads)
        {
            Status = E_INVALIDARG;
        }
        else
        {
            Index = 0;
            for (Thread = g_CurrentProcess->ThreadHead;
                 Thread != NULL;
                 Thread = Thread->Next)
            {
                if (Index >= Start && Index < Start + Count)
                {
                    if (Ids != NULL)
                    {
                        *Ids++ = Thread->UserId;
                    }
                    if (SysIds != NULL)
                    {
                        *SysIds++ = Thread->SystemId;
                    }
                }

                Index++;
            }

            Status = S_OK;
        }
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetThreadIdByProcessor(
    THIS_
    IN ULONG Processor,
    OUT PULONG Id
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        ULONG SysId;

        Status = g_Target->GetThreadIdByProcessor(Processor, &SysId);
        if (Status == S_OK)
        {
            PTHREAD_INFO Thread =
                FindThreadBySystemId(g_CurrentProcess, SysId);
            if (Thread != NULL)
            {
                *Id = Thread->UserId;
            }
            else
            {
                Status = E_NOINTERFACE;
            }
        }
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentThreadDataOffset(
    THIS_
    OUT PULONG64 Offset
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        Status = g_Target->
            GetThreadInfoDataOffset(g_CurrentProcess->CurrentThread,
                                    0, Offset);
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetThreadIdByDataOffset(
    THIS_
    IN ULONG64 Offset,
    OUT PULONG Id
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        PTHREAD_INFO Thread;

        Status = E_NOINTERFACE;
        for (Thread = g_CurrentProcess->ThreadHead;
             Thread != NULL;
             Thread = Thread->Next)
        {
            HRESULT Status;
            ULONG64 DataOffset;

            Status = g_Target->GetThreadInfoDataOffset(Thread, 0, &DataOffset);
            if (Status != S_OK)
            {
                break;
            }

            if (DataOffset == Offset)
            {
                *Id = Thread->UserId;
                Status = S_OK;
                break;
            }
        }
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentThreadTeb(
    THIS_
    OUT PULONG64 Offset
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        Status = g_Target->GetThreadInfoTeb(g_CurrentProcess->CurrentThread,
                                            0, 0, Offset);
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetThreadIdByTeb(
    THIS_
    IN ULONG64 Offset,
    OUT PULONG Id
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        PTHREAD_INFO Thread;

        Status = E_NOINTERFACE;
        for (Thread = g_CurrentProcess->ThreadHead;
             Thread != NULL;
             Thread = Thread->Next)
        {
            HRESULT Status;
            ULONG64 Teb;

            Status = g_Target->GetThreadInfoTeb(Thread, 0, 0, &Teb);
            if (Status != S_OK)
            {
                break;
            }

            if (Teb == Offset)
            {
                *Id = Thread->UserId;
                Status = S_OK;
                break;
            }
        }
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentThreadSystemId(
    THIS_
    OUT PULONG SysId
    )
{
    if (IS_KERNEL_TARGET())
    {
        return E_NOTIMPL;
    }

    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        *SysId = g_CurrentProcess->CurrentThread->SystemId;
        Status = S_OK;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetThreadIdBySystemId(
    THIS_
    IN ULONG SysId,
    OUT PULONG Id
    )
{
    HRESULT Status;

    ENTER_ENGINE();
    
    if (IS_KERNEL_TARGET())
    {
        Status = E_NOTIMPL;
    }
    else if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        PTHREAD_INFO Thread = FindThreadBySystemId(g_CurrentProcess, SysId);
        if (Thread != NULL)
        {
            *Id = Thread->UserId;
            Status = S_OK;
        }
        else
        {
            Status = E_NOINTERFACE;
        }
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentThreadHandle(
    THIS_
    OUT PULONG64 Handle
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        *Handle = g_CurrentProcess->CurrentThread->Handle;
        Status = S_OK;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetThreadIdByHandle(
    THIS_
    IN ULONG64 Handle,
    OUT PULONG Id
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        PTHREAD_INFO Thread = FindThreadByHandle(g_CurrentProcess, Handle);
        if (Thread != NULL)
        {
            *Id = Thread->UserId;
            Status = S_OK;
        }
        else
        {
            Status = E_NOINTERFACE;
        }
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetNumberProcesses(
    THIS_
    OUT PULONG Number
    )
{
    if (!IS_TARGET_SET())
    {
        return E_UNEXPECTED;
    }

    ENTER_ENGINE();

    *Number = g_NumberProcesses;

    LEAVE_ENGINE();
    return S_OK;
}

STDMETHODIMP
DebugClient::GetProcessIdsByIndex(
    THIS_
    IN ULONG Start,
    IN ULONG Count,
    OUT OPTIONAL /* size_is(Count) */ PULONG Ids,
    OUT OPTIONAL /* size_is(Count) */ PULONG SysIds
    )
{
    if (!IS_TARGET_SET())
    {
        return E_UNEXPECTED;
    }

    HRESULT Status;

    ENTER_ENGINE();

    PPROCESS_INFO Process;
    ULONG Index;

    if (Start >= g_NumberProcesses ||
        Start + Count > g_NumberProcesses)
    {
        Status = E_INVALIDARG;
        goto EH_Exit;
    }

    Index = 0;
    for (Process = g_ProcessHead;
         Process != NULL;
         Process = Process->Next)
    {
        if (Index >= Start && Index < Start + Count)
        {
            if (Ids != NULL)
            {
                *Ids++ = Process->UserId;
            }
            if (SysIds != NULL)
            {
                *SysIds++ = Process->SystemId;
            }
        }

        Index++;
    }

    Status = S_OK;

 EH_Exit:
    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentProcessDataOffset(
    THIS_
    OUT PULONG64 Offset
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        Status = g_Target->
            GetProcessInfoDataOffset(g_CurrentProcess->CurrentThread,
                                     0, 0, Offset);
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetProcessIdByDataOffset(
    THIS_
    IN ULONG64 Offset,
    OUT PULONG Id
    )
{
    if (!IS_TARGET_SET())
    {
        return E_UNEXPECTED;
    }
    if (IS_KERNEL_TARGET())
    {
        return E_NOTIMPL;
    }

    HRESULT Status;
    PPROCESS_INFO Process;

    ENTER_ENGINE();

    Status = E_NOINTERFACE;
    for (Process = g_ProcessHead;
         Process != NULL;
         Process = Process->Next)
    {
        ULONG64 DataOffset;

        Status = g_Target->
            GetProcessInfoDataOffset(Process->ThreadHead, 0, 0, &DataOffset);
        if (Status != S_OK)
        {
            break;
        }

        if (DataOffset == Offset)
        {
            *Id = Process->UserId;
            Status = S_OK;
            break;
        }
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentProcessPeb(
    THIS_
    OUT PULONG64 Offset
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        Status = g_Target->GetProcessInfoPeb(g_CurrentProcess->CurrentThread,
                                             0, 0, Offset);
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetProcessIdByPeb(
    THIS_
    IN ULONG64 Offset,
    OUT PULONG Id
    )
{
    if (!IS_TARGET_SET())
    {
        return E_UNEXPECTED;
    }
    if (IS_KERNEL_TARGET())
    {
        return E_NOTIMPL;
    }

    HRESULT Status;
    PPROCESS_INFO Process;

    ENTER_ENGINE();

    Status = E_NOINTERFACE;
    for (Process = g_ProcessHead;
         Process != NULL;
         Process = Process->Next)
    {
        ULONG64 Peb;

        Status = g_Target->GetProcessInfoPeb(Process->ThreadHead, 0, 0, &Peb);
        if (Status != S_OK)
        {
            break;
        }

        if (Peb == Offset)
        {
            *Id = Process->UserId;
            Status = S_OK;
            break;
        }
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentProcessSystemId(
    THIS_
    OUT PULONG SysId
    )
{
    if (IS_KERNEL_TARGET())
    {
        return E_NOTIMPL;
    }

    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        *SysId = g_CurrentProcess->SystemId;
        Status = S_OK;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetProcessIdBySystemId(
    THIS_
    IN ULONG SysId,
    OUT PULONG Id
    )
{
    if (!IS_TARGET_SET())
    {
        return E_UNEXPECTED;
    }
    if (IS_KERNEL_TARGET())
    {
        return E_NOTIMPL;
    }

    HRESULT Status;

    ENTER_ENGINE();

    PPROCESS_INFO Process = FindProcessBySystemId(SysId);
    if (Process != NULL)
    {
        *Id = Process->UserId;
        Status = S_OK;
    }
    else
    {
        Status = E_NOINTERFACE;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentProcessHandle(
    THIS_
    OUT PULONG64 Handle
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        *Handle = (ULONG64)g_CurrentProcess->Handle;
        Status = S_OK;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetProcessIdByHandle(
    THIS_
    IN ULONG64 Handle,
    OUT PULONG Id
    )
{
    if (!IS_TARGET_SET())
    {
        return E_UNEXPECTED;
    }

    HRESULT Status;

    ENTER_ENGINE();

    PPROCESS_INFO Process = FindProcessByHandle(Handle);
    if (Process != NULL)
    {
        *Id = Process->UserId;
        Status = S_OK;
    }
    else
    {
        Status = E_NOINTERFACE;
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentProcessExecutableName(
    THIS_
    OUT OPTIONAL PSTR Buffer,
    IN ULONG BufferSize,
    OUT OPTIONAL PULONG ExeSize
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else if (g_CurrentProcess->ExecutableImage == NULL)
    {
        Status = E_NOINTERFACE;
    }
    else
    {
        Status = FillStringBuffer(g_CurrentProcess->ExecutableImage->ImagePath,
                                  0, Buffer, BufferSize, ExeSize);
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::GetCurrentProcessUpTime(
    THIS_
    OUT PULONG UpTime
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (!IS_LIVE_USER_TARGET() || g_CurrentProcess == NULL)
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        ULONG64 LongUpTime;
        
        LongUpTime = g_Target->GetProcessUpTimeN(g_CurrentProcess->FullHandle);
        if (LongUpTime == 0)
        {
            Status = E_NOINTERFACE;
        }
        else
        {
            *UpTime = FileTimeToTime(LongUpTime);
            Status = S_OK;
        }
    }

    LEAVE_ENGINE();
    return Status;
}

HRESULT
GetContextFromThreadStack(
    ULONG64 ThreadBase,
    PCROSS_PLATFORM_CONTEXT Context,
    PDEBUG_STACK_FRAME StkFrame,
    BOOL Verbose
    )
{
    DBG_ASSERT(ThreadBase && Context != NULL);

    if (!IS_KERNEL_TARGET()) 
    {
        return E_UNEXPECTED;
    }

    HRESULT Status;
    CROSS_PLATFORM_THREAD ThreadContents;
    DEBUG_STACK_FRAME LocalStkFrame;
    ULONG Proc;

    if (!StkFrame) 
    {
        StkFrame = &LocalStkFrame;
    } 

    ZeroMemory(StkFrame, sizeof(*StkFrame));

    if ((Status = g_Target->ReadAllVirtual
         (ThreadBase, &ThreadContents,
          g_TargetMachine->m_SizePartialKThread)) != S_OK)
    {
        {
            ErrOut("Cannot read thread contents @ %s, %s\n",
                   FormatAddr64(ThreadBase), FormatStatusCode(Status));
        }
        return Status;
    }
        
    if (*((PCHAR) &ThreadContents) != 6) 
    {
        ErrOut("Invalid thread @ %s type - context unchanged.\n",
               FormatAddr64(ThreadBase));
        return E_INVALIDARG;
    }

    Status = g_TargetMachine->GetContextFromThreadStack
        (ThreadBase, &ThreadContents, Context, StkFrame, &Proc);
    if (Status == S_FALSE)
    {
        // Get the processor context if it's a valid processor.
        if (Proc < g_TargetNumberProcessors)
        {
            // This get may be getting the context of the thread
            // currently cached by the register code.  Make sure
            // the cache is flushed.
            FlushRegContext();

            g_TargetMachine->InitializeContextFlags(Context, g_SystemVersion);
            if ((Status = g_Target->GetContext(VIRTUAL_THREAD_HANDLE(Proc),
                                               Context)) != S_OK)
            {
                ErrOut("Unable to get context for thread "
                       "running on processor %d, %s\n",
                       Proc, FormatStatusCode(Status));
                return Status;
            }
        }
        else 
        {
            if (Verbose)
            {
                ErrOut("Thread running on invalid processor %d\n", Proc);
            }
            return E_INVALIDARG;
        }
    }
    else if (Status != S_OK)
    {
        if (Verbose)
        {
            ErrOut("Can't retrieve thread context, %s\n", 
                   FormatStatusCode(Status));
        }
        return Status;
    }

    return S_OK;
}

HRESULT
SetContextFromThreadData(ULONG64 ThreadBase, BOOL Verbose)
{
    if (!ThreadBase) 
    {
        if (GetCurrentScopeContext())
        {
            ResetCurrentScope();
        }
        return S_OK;
    }

    HRESULT Status;
    DEBUG_STACK_FRAME StkFrame = {0};
    CROSS_PLATFORM_CONTEXT Context = {0};
    
    if ((Status = GetContextFromThreadStack(ThreadBase, &Context,
                                            &StkFrame, FALSE)) != S_OK)
    {
        return Status;
    }
    
    SetCurrentScope(&StkFrame, &Context, sizeof(Context));
    if (StackTrace(StkFrame.FrameOffset, StkFrame.StackOffset,
                   StkFrame.InstructionOffset,
                   &StkFrame, 1, 0, 0, FALSE) != 1)
    {
        if (Verbose) 
        {
            ErrOut("Scope can't be set for thread %s\n",
                   FormatAddr64(ThreadBase));
        }
        ResetCurrentScope();
        return E_FAIL;
    }

    return S_OK;
}

HRESULT
GetImplicitThreadData(PULONG64 Offset)
{
    HRESULT Status;
    
    if (g_ImplicitThreadData == 0)
    {
        Status = SetImplicitThreadData(0, FALSE);
    }
    else
    {
        Status = S_OK;
    }
    
    *Offset = g_ImplicitThreadData;
    return Status;
}

HRESULT
GetImplicitThreadDataTeb(PULONG64 Offset)
{
    if (IS_USER_TARGET())
    {
        // In user mode the thread data is the TEB.
        return GetImplicitThreadData(Offset);
    }
    else
    {
        return g_Target->ReadImplicitThreadInfoPointer
            (g_TargetMachine->m_OffsetKThreadTeb, Offset);
    }
}

HRESULT
SetImplicitThreadData(ULONG64 Offset, BOOL Verbose)
{
    HRESULT Status;
    BOOL Default = FALSE;
    
    if (Offset == 0)
    {
        if ((Status = g_Target->
             GetThreadInfoDataOffset(g_CurrentProcess->CurrentThread,
                                     0, &Offset)) != S_OK)
        {
            if (Verbose)
            {
                ErrOut("Unable to get the current thread data\n");
            }
            return Status;
        }
        if (Offset == 0)
        {
            if (Verbose)
            {
                ErrOut("Current thread data is NULL\n");
            }
            return E_FAIL;
        }

        Default = TRUE;
    }

    if (IS_KERNEL_TARGET() &&
        (Status = SetContextFromThreadData(Default ? 0 : Offset,
                                           Verbose)) != S_OK)
    {
        return Status;
    }
    
    g_ImplicitThreadData = Offset;
    g_ImplicitThreadDataIsDefault = Default;
    return S_OK;
}

STDMETHODIMP
DebugClient::GetImplicitThreadDataOffset(
    THIS_
    OUT PULONG64 Offset
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (!IS_MACHINE_ACCESSIBLE())
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        Status = ::GetImplicitThreadData(Offset);
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::SetImplicitThreadDataOffset(
    THIS_
    IN ULONG64 Offset
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (!IS_MACHINE_ACCESSIBLE())
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        Status = ::SetImplicitThreadData(Offset, FALSE);
    }

    LEAVE_ENGINE();
    return Status;
}

HRESULT
GetImplicitProcessData(PULONG64 Offset)
{
    HRESULT Status;
    
    if (g_ImplicitProcessData == 0)
    {
        Status = SetImplicitProcessData(0, FALSE);
    }
    else
    {
        Status = S_OK;
    }
    
    *Offset = g_ImplicitProcessData;
    return Status;
}

HRESULT
GetImplicitProcessDataPeb(PULONG64 Offset)
{
    if (IS_USER_TARGET())
    {
        // In user mode the process data is the PEB.
        return GetImplicitProcessData(Offset);
    }
    else
    {
        return g_Target->ReadImplicitProcessInfoPointer
            (g_TargetMachine->m_OffsetEprocessPeb, Offset);
    }
}

HRESULT
SetImplicitProcessData(ULONG64 Offset, BOOL Verbose)
{
    HRESULT Status;
    BOOL Default = FALSE;

    if (Offset == 0)
    {
        if ((Status = g_Target->
             GetProcessInfoDataOffset(g_CurrentProcess->CurrentThread,
                                      0, 0, &Offset)) != S_OK)
        {
            if (Verbose)
            {
                ErrOut("Unable to get the current process data\n");
            }
            return Status;
        }
        if (Offset == 0)
        {
            if (Verbose)
            {
                ErrOut("Current process data is NULL\n");
            }
            return E_FAIL;
        }

        Default = TRUE;
    }
    
    ULONG64 Old = g_ImplicitProcessData;
    BOOL OldDefault = g_ImplicitProcessDataIsDefault;
        
    g_ImplicitProcessData = Offset;
    g_ImplicitProcessDataIsDefault = Default;
    if (IS_KERNEL_TARGET() &&
        (Status = g_TargetMachine->
         SetDefaultPageDirectories(PAGE_DIR_ALL)) != S_OK)
    {
        g_ImplicitProcessData = Old;
        g_ImplicitProcessDataIsDefault = OldDefault;
        if (Verbose)
        {
            ErrOut("Process %s has invalid page directories\n",
                   FormatAddr64(Offset));
        }

        return Status;
    }

    return S_OK;
}

STDMETHODIMP
DebugClient::GetImplicitProcessDataOffset(
    THIS_
    OUT PULONG64 Offset
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (!IS_MACHINE_ACCESSIBLE())
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        Status = ::GetImplicitProcessData(Offset);
    }

    LEAVE_ENGINE();
    return Status;
}

STDMETHODIMP
DebugClient::SetImplicitProcessDataOffset(
    THIS_
    IN ULONG64 Offset
    )
{
    HRESULT Status;

    ENTER_ENGINE();

    if (!IS_MACHINE_ACCESSIBLE())
    {
        Status = E_UNEXPECTED;
    }
    else
    {
        Status = ::SetImplicitProcessData(Offset, FALSE);
    }

    LEAVE_ENGINE();
    return Status;
}

void
ResetImplicitData(void)
{
    g_ImplicitThreadData = 0;
    g_ImplicitThreadDataIsDefault = TRUE;
    g_ImplicitProcessData = 0;
    g_ImplicitProcessDataIsDefault = TRUE;
}

void
ParseSetImplicitThread(void)
{
    ULONG64 Base = 0;

    if (PeekChar() && *g_CurCmd != ';')
    {
        Base = GetExpression();
    }

    if (SetImplicitThreadData(Base, TRUE) == S_OK)
    {
        dprintf("Implicit thread is now %s\n",
                FormatAddr64(g_ImplicitThreadData));
    }
}

HRESULT
KernelPageIn(ULONG64 Process, ULONG64 Data)
{
    HRESULT Status;
    ULONG work;
    ULONG Size;

    ULONG64 ExpDebuggerProcessAttach = 0;
    ULONG64 ExpDebuggerPageIn = 0;
    ULONG64 ExpDebuggerWork = 0;

    if (!IS_LIVE_KERNEL_TARGET())
    {
        ErrOut("This operation only works on live kernel debug sessions\n");
        return E_NOTIMPL;
    }

    GetOffsetFromSym("nt!ExpDebuggerProcessAttach", &ExpDebuggerProcessAttach, NULL);
    GetOffsetFromSym("nt!ExpDebuggerPageIn", &ExpDebuggerPageIn, NULL);
    GetOffsetFromSym("nt!ExpDebuggerWork", &ExpDebuggerWork, NULL);

    if (!ExpDebuggerProcessAttach ||
        !ExpDebuggerPageIn        ||
        !ExpDebuggerWork)
    {
        ErrOut("Symbols are wrong or this version of the operating system"
               "does not support this command\n");
        return E_NOTIMPL;
    }

    Status = g_Target->ReadVirtual(ExpDebuggerWork, &work, sizeof(work), &Size);

    if ((Status != S_OK) || Size != sizeof(work))
    {
        ErrOut("Could not determine status or debugger worker thread\n");
        return HRESULT_FROM_WIN32(ERROR_BUSY);
    }
    else if (work > 1)
    {
        ErrOut("Debugger worker thread has pending command\n");
        return HRESULT_FROM_WIN32(ERROR_BUSY);
    }

    Status = g_Target->WritePointer(g_Machine, ExpDebuggerProcessAttach,
                                    Process);
    if (Status == S_OK)
    {
        Status = g_Target->WritePointer(g_Machine, ExpDebuggerPageIn,
                                        Data);
    }

    if (Status == S_OK)
    {
        work = 1;
        Status = g_Target->WriteVirtual(ExpDebuggerWork, &work,
                                        sizeof(work), &Size);
    }

    if (Status != S_OK)
    {
        ErrOut("Could not queue operation to debugger worker thread\n");
    }

    return Status;
}



void
ParseSetImplicitProcess(void)
{
    ULONG64 Base = 0;
    BOOL Ptes = FALSE;
    BOOL Invasive = FALSE;
    BOOL OldPtes = g_VirtualCache.m_ForceDecodePTEs;

    while (PeekChar() == '-' || *g_CurCmd == '/')
    {
        switch(*(++g_CurCmd))
        {
        case 'p':
            Ptes = TRUE;
            break;
        case 'i':
            Invasive = TRUE;
            break;
        default:
            dprintf("Unknown option '%c'\n", *g_CurCmd);
            break;
        }

        g_CurCmd++;
    }
    
    if (PeekChar() && *g_CurCmd != ';')
    {
        Base = GetExpression();
    }

    if (Invasive)
    {
        if (S_OK == KernelPageIn(Base, 0))
        {
            dprintf("You need to continue execution (press 'g' <enter>) for "
                    "the context to be switched.  When the debugger breaks "
                    "in again, you will be in the new process context.\n");
        }
        return;
    }
    if (Ptes && !Base)
    {
        // If the user has requested a reset to the default
        // process with no translations we need to turn
        // off translations immediately so that any
        // existing base doesn't interfere with determining
        // the default process.
        g_VirtualCache.SetForceDecodePtes(FALSE);
    }
        
    if (SetImplicitProcessData(Base, TRUE) == S_OK)
    {
        dprintf("Implicit process is now %s\n",
                FormatAddr64(g_ImplicitProcessData));

        if (Ptes)
        {
            if (Base)
            {
                g_VirtualCache.SetForceDecodePtes(TRUE);
            }
            dprintf(".cache %sforcedecodeptes done\n",
                    Base != 0 ? "" : "no");
        }
        
        if (Base && !g_VirtualCache.m_ForceDecodePTEs)
        {
            WarnOut("WARNING: .cache forcedecodeptes is not enabled\n");
        }
    }
    else if (Ptes && !Base && OldPtes)
    {
        // Restore settings to the way they were.
        g_VirtualCache.SetForceDecodePtes(TRUE);
    }
}

void
ParsePageIn(void)
{
    ULONG64 Process = 0;
    ULONG64 Data = 0;

    while (PeekChar() == '-' || *g_CurCmd == '/')
    {
        switch(*(++g_CurCmd))
        {
        case 'p':
            g_CurCmd++;
            Process = GetExpression();
            break;
        default:
            g_CurCmd++;
            dprintf("Unknown option '%c'\n", *g_CurCmd);
            break;
        }
    }
    
    if (PeekChar() && *g_CurCmd != ';')
    {
        Data = GetExpression();
    }

    if (!Data)
    {
        ErrOut("Pagein requires an address to be specified\n");
        return;
    }

    //
    // Modify kernel state to do the pagein
    //

    if (S_OK != KernelPageIn(Process, Data))
    {
        ErrOut("PageIn for address %s, process %s failed\n",
               FormatAddr64(Data),
               FormatAddr64(Process));
    }
    else
    {
        dprintf("You need to continue execution (press 'g' <enter>) for "
                 "the pagein to be brought in.  When the debugger breaks in "
                 "again, the page will be present.\n");
    }

}