//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//------------------------------------------------------------------------------
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//  
//------------------------------------------------------------------------------
//
//  Module Name:  
//  
//      celognk.h
//  
//  Abstract:  
//
//      Kernel's interface to the event logging functions.
//      
//------------------------------------------------------------------------------
#ifndef __CELOGNK_H_
#define __CELOGNK_H_


// This must match the table in kcall.h.
// It is defined here so that it can be shared with readlog.
#define MAX_KCALL_PROFILE 77


#if defined(IN_KERNEL) && !defined(NK_LOADER)


//==============================================================================
//
//  Logging operation
//
//==============================================================================

// Include the public SDK structures.                
#include <celog.h>

// Include exported profiler functions
#include <profiler.h>

#define MAX_CALLSTACK   20

// Globals that allow a logging engine to be hot-plugged in the system.
#ifdef __cplusplus
extern void (*g_pfnCeLogData)(...);
#else
extern void (*g_pfnCeLogData)();
#endif
extern PFNVOID g_pfnCeLogInterrupt;
extern PFNVOID g_pfnCeLogSetZones;


#undef IsCeLogStatus
#undef IsCeLogEnabled
#undef IsCeLogZoneEnabled
#define IsCeLogStatus(Status)           ((KInfoTable[KINX_CELOGSTATUS] & (Status)) != 0)
#define IsCeLogEnabled(Status, ZoneCE)  (IsCeLogStatus(Status) && ((KInfoTable[KINX_CELOGSTATUS] & (ZoneCE)) != 0))
#define IsCeLogZoneEnabled(ZoneCE)      (IsCeLogEnabled(CELOGSTATUS_ENABLED_GENERAL, (ZoneCE)))

// Set the proper status and zones
#define CeLogEnable(Status, ZoneCE)     (KInfoTable[KINX_CELOGSTATUS] = ((Status) | ((ZoneCE) & ~CELOGSTATUS_ENABLED_ANY)))
// Set the proper status without changing the zones
#define CeLogEnableStatus(Status)       (CeLogEnable((Status), KInfoTable[KINX_CELOGSTATUS] & ~CELOGSTATUS_ENABLED_ANY))
// Set the proper zones without changing the status
#define CeLogEnableZones(ZoneCE)        (CeLogEnable(KInfoTable[KINX_CELOGSTATUS] & CELOGSTATUS_ENABLED_ANY, (ZoneCE)))
// Disable all logging
#define CeLogDisable()                  (KInfoTable[KINX_CELOGSTATUS] = 0)


// Kernel logger functions
void NKCeLogData(BOOL fTimeStamp, DWORD dwID, VOID *pData, DWORD dwLen, DWORD dwZoneUser, DWORD dwZoneCE, DWORD dwFlag, BOOL fFlagged);
void NKCeLogSetZones(DWORD dwZoneUser, DWORD dwZoneCE, DWORD dwZoneProcess);
BOOL NKCeLogGetZones(LPDWORD lpdwZoneUser, LPDWORD lpdwZoneCE, LPDWORD lpdwZoneProcess, LPDWORD lpdwAvailableZones);
BOOL NKCeLogReSync();

void LoggerInit (void);

BOOL KernelLibIoControl_CeLog(
    DWORD   dwIoControlCode,
    LPVOID  lpInBuf,
    DWORD   nInBufSize,
    LPVOID  lpOutBuf,
    DWORD   nOutBufSize,
    LPDWORD lpBytesReturned
    );


//==============================================================================
//
// Default zone settings
//
//==============================================================================

#ifndef CELOG_USER_MASK
#define CELOG_USER_MASK    0xFFFFFFFF
#endif

// KCall profiling is turned off by default to prevent overwhelming logs
// GWES events are turned off by default to prevent data the user doesn't know
#ifndef CELOG_CE_MASK
#define CELOG_CE_MASK      0xFFBFDFFF
#endif

#ifndef CELOG_PROCESS_MASK
#define CELOG_PROCESS_MASK 0xFFFFFFFF
#endif


//==============================================================================
//
// Interface to a CeLog plugin DLL
//
//==============================================================================

#define CELOG_IMPORT_VERSION            4
#define CELOG_PRIVATE_IMPORT_VERSION    1
#define CELOG_EXPORT_VERSION            2

typedef LONG (*PFN_RegQueryValueExW) (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD);
typedef BOOL (*PFN_INTERRUPTS_ENABLE) (BOOL);
typedef LPVOID (*PFN_Pfn2Virt) (DWORD);
typedef int (*PFN_KCall) (PKFN, ...);
typedef void (*PFN_CeLogData) (BOOL, DWORD, PVOID, DWORD, DWORD, DWORD, DWORD, BOOL);


// Private kernel functions and data provided for CeLog to use.
// Queried with IOCTL_CELOG_IMPORT_PRIVATE.
// This structure may change on future OS versions.
typedef struct _CeLogPrivateImports {
    DWORD    dwVersion;                       // Version of this structure, set to CELOG_PRIVATE_IMPORT_VERSION
    PFN_RegQueryValueExW  pRegQueryValueExW;  // Pointer to NKRegQueryValueExW function
    PFN_INTERRUPTS_ENABLE pINTERRUPTS_ENABLE; // Pointer to INTERRUPTS_ENABLE function
    PFN_Pfn2Virt          pPfn2Virt;          // Pointer to Pfn2Virt wrapper function
    PFN_KCall             pKCall;             // Pointer to KCall function
    PFN_CeLogData         pCeLogData;         // Pointer to kernel's CeLogData entry point
    struct KDataStruct   *pKData;             // Pointer to KData struct
} CeLogPrivateImports;


//==============================================================================
//
// The instrumentation that logs all the kernel CeLog events.
//
//==============================================================================

void CELOG_Interrupt(DWORD dwLogValue);
void CELOG_OutputDebugString(DWORD dwProcessId, DWORD dwThreadId, LPCWSTR pszMessage);
void CELOG_ThreadMigrate(DWORD dwProcessId);
void CELOG_KCThreadSwitch(PTHREAD pThread);
void CELOG_KCThreadRunnable(PTHREAD pThread);
void CELOG_KCThreadQuantumExpire();
void CELOG_KCThreadPriorityInvert(PTHREAD pThread);
void CELOG_KCThreadSetPriority(PTHREAD pThread, int nPriority);
void CELOG_ThreadSetQuantum(PTHREAD pThread, DWORD dwQuantum);
void CELOG_KCThreadSuspend(PTHREAD pThread);
void CELOG_KCThreadResume(PTHREAD pThread);
void CELOG_ThreadCreate(PTHREAD pThread, PPROCESS pProcess, LPVOID lpvThreadParam);
void CELOG_ThreadTerminate(PTHREAD pThread);
void CELOG_ThreadDelete(PTHREAD pThread);
void CELOG_ProcessCreateEx(PPROCESS pProc);
void CELOG_ProcessTerminate(PPROCESS pProcess);
void CELOG_ProcessDelete(PPROCESS pProcess);
void CELOG_Sleep(DWORD dwTimeout);
void CELOG_OpenOrCreateHandle(PHDATA phd, BOOL fCreate);
void CELOG_EventModify(PEVENT pEvent, DWORD type);
void CELOG_EventDelete(PEVENT pEvent);
void CELOG_WaitForMultipleObjects(DWORD cObjects, PHDATA* phds, DWORD dwTimeout);
void CELOG_MutexRelease(PMUTEX pMutex);
void CELOG_MutexDelete(PMUTEX pMutex);
void CELOG_SemaphoreRelease(PSEMAPHORE pSemaphore, LONG lReleaseCount, LONG lPreviousCount);
void CELOG_SemaphoreDelete(PSEMAPHORE pSemaphore);
void CELOG_CriticalSectionDelete(PCRIT pCriticalSection);
void CELOG_CriticalSectionEnter(PCRIT pCriticalSection);
void CELOG_CriticalSectionLeave(PCRIT pCriticalSection, PTHREAD pNewOwnerThread);
void CELOG_VirtualAlloc(PPROCESS pProcess, DWORD dwResult, DWORD dwAddress, DWORD cPages, DWORD dwType, DWORD dwProtect);
void CELOG_VirtualCopy(PPROCESS pProcessDest, DWORD dwDest, PPROCESS pProcessSrc, DWORD dwSource, DWORD cPages, DWORD dwPfn, DWORD dwProtect);
void CELOG_VirtualFree(PPROCESS pProcess, DWORD dwAddress, DWORD cPages, DWORD dwType);
void CELOG_MapFileCreate(PFSMAP pMap, PNAME pMapName);
void CELOG_MapFileDestroy(PFSMAP pMap);
void CELOG_MapFileViewOpen(PPROCESS pProcess, PMAPVIEW pView);
void CELOG_MapFileViewClose(PPROCESS pProcess, PMAPVIEW pView);
void CELOG_MapFileViewFlush(PPROCESS pProcess, PFSMAP pfsmap, LPBYTE pFlushStart, DWORD cbSize, DWORD cDirtyPages, BOOL fBegin);
void CELOG_MapFileValidate(DWORD cbSize, DWORD dwRestoreSize, DWORD dwRestoreFlags, BOOL fBegin);
void CELOG_PageFault(PPROCESS pProcess, DWORD dwAddress, BOOL fBegin, BOOL fOtherFlag);
void CELOG_ModuleLoad(PPROCESS pProcess, PMODULE pModule);
void CELOG_ModuleFree(PPROCESS pProcess, PMODULE pModule);
void CELOG_LaunchingFilesys();

BOOL KCallCeLogReSync();

// Special functions used only by CELOG_ReSync, to minimize stack usage
void CELOG_SyncProcess(PPROCESS pProcess);
void CELOG_SyncThread(PTHREAD pThread, PPROCESS pProcess);
void CELOG_SyncModule(PMODULE pModule);
BOOL CELOG_SyncModuleRefCount(PMODULELIST pml, PPROCESS pProcess, BOOL Reset);



//==============================================================================
//
// Special events that are only logged in profiling builds, to avoid the
// performance hit they'd cause.
//
//==============================================================================

#ifdef NKPROF

#define CELOG_KCallEnter(nID)                                                  \
    g_pfnCeLogData(TRUE, CELID_KCALL_ENTER, &nID, sizeof(int), 0, CELZONE_KCALL, 0, FALSE)

#define CELOG_KCallLeave(nID)                                                  \
    g_pfnCeLogData(TRUE, CELID_KCALL_LEAVE, &nID, sizeof(int), 0, CELZONE_KCALL, 0, FALSE)

#else // NKPROF

#define CELOG_KCallEnter(nID)                                          ((void)0)
#define CELOG_KCallLeave(nID)                                          ((void)0)

#endif // NKPROF



//==============================================================================
//
//  Profiler definitions
//
//==============================================================================

void GetThreadName(
    PTHREAD pth,
    HANDLE* hModule,
    WCHAR*  szThreadFunctionName
    );

void
NKProfileSyscall(
    const VOID* pControl,       // I_PTR, variable-sized struct
    DWORD cbControl
    );


typedef struct {
    BOOL  bProfileTimerRunning;
    DWORD dwProfilerFlags;
    
    // State used inside ProfileStart/ProfileStop
    int   scPauseContinueCalls;
    BOOL  bStart;
    struct {
        DWORD dwZoneUser;
        DWORD dwZoneCE;
        DWORD dwZoneProcess;
    } SavedCeLogZones;

    // Used for symbol lookup
    DWORD dwNumROMModules;      // Number of modules in all XIP regions

    // Total number of profiler samples is dwSamplesRecorded + dwSamplesDropped + dwSamplesInIdle
    DWORD dwSamplesRecorded;    // Number of profiler hits that were recorded in the buffer or symbol list
    DWORD dwSamplesDropped;     // Number of profiler hits that were not recorded due to buffer overrun

    // System state that is tracked while the profiler is running in Monte Carlo mode.
    DWORD dwSamplesInIdle;      // Number of profiler samples when no threads were scheduled
    DWORD dwInterrupts;         // Number of interrupts that occurred while profiling, including profiler interrupt
    
    // System state counters that will run when profiling is paused are tracked by 
    // recording start values while running and elapsed values during the pause.
    DWORD dwTLBCount;           // Used to count TLB misses while profiling
    DWORD dwTickCount;          // Used to count ticks while profiling
} ProfilerState;

extern ProfilerState g_ProfilerState;


// Number of TLB misses that had profiler interrupts pending.  Tracked while
// the profiler is running in Monte Carlo mode.  This value is not stored in
// the ProfilerState structure because it is more easily accessed from assembly
// code this way.
extern DWORD g_ProfilerState_dwProfilerIntsInTLB;

// These values can be overwritten as FIXUPVARs in config.bib
extern const volatile DWORD dwProfileBufferMax;         // Size of profiling buffer to allocate (if possible)
extern const volatile DWORD dwProfileBootUSecInterval;  // Interval to pass to ProfileStart() on boot
extern const volatile DWORD dwProfileBootOptions;       // Flags to pass to ProfileStart() on boot


#ifdef NKPROF

extern DWORD PROF_GetEPC(void);
extern void  PROF_ProfilerHitEx(OEMProfilerData *pData) ;
extern void  PROF_ProfileStartEx(const ProfilerControl* pControl);
extern void  PROF_ProfileStop(const ProfilerControl* pControl);
extern void  PROF_ProfileCaptureStatus(const ProfilerControl* pControl);
extern void  PROF_GetThreadName(PTHREAD pth, HANDLE* hModule, WCHAR* szThreadFunctionName);

#else  // NKPROF

#define PROF_GetEPC()                   (0)
#define PROF_ProfilerHitEx()
#define PROF_ProfilerStartEx()
#define PROF_ProfilerStop()
#define PROF_ProfilerCaptureStatus()

#endif // NKPROF


//==============================================================================
// KCall Profiling
//==============================================================================

#ifdef NKPROF

typedef struct KPRF_t {
    DWORD hits;
    DWORD max;
    DWORD min;
    DWORD total;
    DWORD tmp;
} KPRF_t;

extern KPRF_t KPRFInfo[MAX_KCALL_PROFILE];

_inline void KCALLPROFON(int IND) {
    LARGE_INTEGER liPerf;
    if (g_ProfilerState.dwProfilerFlags & PROFILE_KCALL) {
        DEBUGCHK(InSysCall());
        DEBUGCHK(!KPRFInfo[IND].tmp);
        DEBUGCHK(IND<MAX_KCALL_PROFILE);
        NKQueryPerformanceCounter(&liPerf);
        KPRFInfo[IND].tmp = liPerf.LowPart;
    }
    if (IsCeLogZoneEnabled(CELZONE_KCALL)) {
        CELOG_KCallEnter(IND);
    }
}

_inline void KCALLPROFOFF(int IND) {
    LARGE_INTEGER liPerf;
    DWORD t2;
    if (g_ProfilerState.dwProfilerFlags & PROFILE_KCALL) {
        DEBUGCHK(InSysCall());
        DEBUGCHK(KPRFInfo[IND].tmp);
        DEBUGCHK(IND<MAX_KCALL_PROFILE);
        NKQueryPerformanceCounter(&liPerf);
        t2 = liPerf.LowPart - KPRFInfo[IND].tmp;
        KPRFInfo[IND].tmp = 0;
        if (t2 > KPRFInfo[IND].max)
            KPRFInfo[IND].max = t2;
        if (t2 && (!KPRFInfo[IND].min || (t2 < KPRFInfo[IND].min)))
            KPRFInfo[IND].min = t2;
        KPRFInfo[IND].total += t2;
        KPRFInfo[IND].hits++;
    }
    if (IsCeLogZoneEnabled(CELZONE_KCALL)) {
        CELOG_KCallLeave(IND);
    }
}

#else // NKPROF

#define KCALLPROFON(IND) 0
#define KCALLPROFOFF(IND) 0

#endif // NKPROF

//==============================================================================


#endif // IN_KERNEL

#endif // __CELOGNK_H_
