Files
yttrium-virtio-gpu/NetKVM/Common/ParaNdis_Debug.cpp
T
Yuri Benditovich 90dfc5cc2d netkvm: disable crash callback for Win10 build
Registration of crash callback makes the SDV to skip real check
for NDIS rules. For now, disable crash callback for Win10 build.
If crash callback is needed for some support/disagnostics activity,
8.1 driver can be used for that.

Signed-off-by: Yuri Benditovich <[email protected]>
2020-05-11 23:05:52 +03:00

419 lines
14 KiB
C++
Executable File

/*
* This file contains debug support procedures, common for NDIS5 and NDIS6
*
* Copyright (c) 2008-2017 Red Hat, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of their contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ndis56common.h"
#include "ParaNdis_DebugHistory.h"
#include "Trace.h"
#ifdef NETKVM_WPP_ENABLED
#include "ParaNdis_Debug.tmh"
#endif
extern "C"
{
#include "stdarg.h"
#include "ntstrsafe.h"
}
int virtioDebugLevel = 0;
int bDebugPrint = 1;
/* Crash callback support makes SDV to skip all the tests
* of NDIS entry points and report them as 'Not applicable'
* Leave it enabled for 8.1 and private builds without WPP
*/
//#define ENABLE_CRASH_CALLBACK 1
#if !NDIS_SUPPORT_NDIS680 || !defined(NETKVM_WPP_ENABLED)
#define ENABLE_CRASH_CALLBACK 1
#endif
static NDIS_SPIN_LOCK CrashLock;
static KBUGCHECK_REASON_CALLBACK_ROUTINE ParaNdis_OnBugCheck;
static VOID ParaNdis_PrepareBugCheckData();
typedef BOOLEAN (*KeRegisterBugCheckReasonCallbackType) (
__out PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
__in PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
__in KBUGCHECK_CALLBACK_REASON Reason,
__in PUCHAR Component
);
typedef BOOLEAN (*KeDeregisterBugCheckReasonCallbackType) (
__inout PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
);
typedef ULONG (*vDbgPrintExType)(
__in ULONG ComponentId,
__in ULONG Level,
__in PCCH Format,
__in va_list arglist
);
static ULONG DummyPrintProcedure(
__in ULONG ComponentId,
__in ULONG Level,
__in PCCH Format,
__in va_list arglist
)
{
UNREFERENCED_PARAMETER(ComponentId);
UNREFERENCED_PARAMETER(Level);
UNREFERENCED_PARAMETER(Format);
UNREFERENCED_PARAMETER(arglist);
return 0;
}
static BOOLEAN KeRegisterBugCheckReasonCallbackDummyProc(
__out PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
__in PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
__in KBUGCHECK_CALLBACK_REASON Reason,
__in PUCHAR Component
)
{
UNREFERENCED_PARAMETER(CallbackRoutine);
UNREFERENCED_PARAMETER(Reason);
UNREFERENCED_PARAMETER(Component);
CallbackRecord->State = 0;
return FALSE;
}
BOOLEAN KeDeregisterBugCheckReasonCallbackDummyProc(
__inout PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
)
{
UNREFERENCED_PARAMETER(CallbackRecord);
return FALSE;
}
static vDbgPrintExType PrintProcedure = DummyPrintProcedure;
static KeRegisterBugCheckReasonCallbackType BugCheckRegisterCallback = KeRegisterBugCheckReasonCallbackDummyProc;
static KeDeregisterBugCheckReasonCallbackType BugCheckDeregisterCallback = KeDeregisterBugCheckReasonCallbackDummyProc;
KBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord;
#ifndef NETKVM_WPP_ENABLED
static void NetKVMDebugPrint(const char *fmt, ...)
{
va_list list;
va_start(list, fmt);
PrintProcedure(DPFLTR_DEFAULT_ID, 9 | DPFLTR_MASK, fmt, list);
#if defined(VIRTIO_DBG_USE_IOPORT)
{
NTSTATUS status;
// use this way of output only for DISPATCH_LEVEL,
// higher requires more protection
if (KeGetCurrentIrql() <= DISPATCH_LEVEL)
{
char buf[256];
size_t len, i;
buf[0] = 0;
status = RtlStringCbVPrintfA(buf, sizeof(buf), fmt, list);
if (status == STATUS_SUCCESS) len = strlen(buf);
else if (status == STATUS_BUFFER_OVERFLOW) len = sizeof(buf);
else { memcpy(buf, "Can't print", 11); len = 11; }
NdisAcquireSpinLock(&CrashLock);
for (i = 0; i < len; ++i)
{
NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, buf[i]);
}
NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, '\n');
NdisReleaseSpinLock(&CrashLock);
}
}
#endif
va_end(list);
}
#else
static void NetKVMDebugPrint(const char *fmt, ...)
{
va_list list;
va_start(list, fmt);
char buf[256];
buf[0] = 0;
_vsnprintf_s(buf, sizeof(buf), _TRUNCATE, fmt, list);
TraceNoPrefix(0, "%s", buf);
}
#endif
tDebugPrintFunc VirtioDebugPrintProc = NetKVMDebugPrint;
void ParaNdis_DebugInitialize()
{
NDIS_STRING usRegister, usDeregister, usPrint;
PVOID pr, pd;
BOOLEAN res = false;
NdisAllocateSpinLock(&CrashLock);
KeInitializeCallbackRecord(&CallbackRecord);
ParaNdis_PrepareBugCheckData();
NdisInitUnicodeString(&usPrint, L"vDbgPrintEx");
NdisInitUnicodeString(&usRegister, L"KeRegisterBugCheckReasonCallback");
NdisInitUnicodeString(&usDeregister, L"KeDeregisterBugCheckReasonCallback");
#ifndef NETKVM_WPP_ENABLED
pd = MmGetSystemRoutineAddress(&usPrint);
if (pd) PrintProcedure = (vDbgPrintExType)pd;
#endif
pr = MmGetSystemRoutineAddress(&usRegister);
pd = MmGetSystemRoutineAddress(&usDeregister);
if (pr && pd)
{
BugCheckRegisterCallback = (KeRegisterBugCheckReasonCallbackType)pr;
BugCheckDeregisterCallback = (KeDeregisterBugCheckReasonCallbackType)pd;
}
#if ENABLE_CRASH_CALLBACK
res = BugCheckRegisterCallback(&CallbackRecord, ParaNdis_OnBugCheck, KbCallbackSecondaryDumpData, (const PUCHAR)"NetKvm");
#endif
DPrintf(0, "[%s] Crash callback %sregistered\n", __FUNCTION__, res ? "" : "NOT ");
}
void ParaNdis_DebugCleanup(PDRIVER_OBJECT pDriverObject)
{
UNREFERENCED_PARAMETER(pDriverObject);
BugCheckDeregisterCallback(&CallbackRecord);
}
#define MAX_CONTEXTS 4
#if defined(ENABLE_HISTORY_LOG)
#define MAX_HISTORY 0x40000
#else
#define MAX_HISTORY 2
#endif
#if defined(KEEP_PENDING_NBL)
#define MAX_KEEP_NBLS 1024
#else
#define MAX_KEEP_NBLS 1
#endif
typedef struct _tagBugCheckStaticData
{
tBugCheckStaticDataHeader Header;
tBugCheckPerNicDataContent PerNicData[MAX_CONTEXTS];
tBugCheckStaticDataContent Data;
tBugCheckHistoryDataEntry History[MAX_HISTORY];
RTL_BITMAP PendingNblsBitmap;
ULONG PendingNblsBitmapBuffer[MAX_KEEP_NBLS/32 + !!(MAX_KEEP_NBLS%32)];
tPendingNBlEntry PendingNbls[MAX_KEEP_NBLS];
}tBugCheckStaticData;
typedef struct _tagBugCheckData
{
tBugCheckStaticData StaticData;
tBugCheckDataLocation Location;
}tBugCheckData;
static tBugCheckData BugCheckData;
static BOOLEAN bNative = TRUE;
VOID ParaNdis_PrepareBugCheckData()
{
BugCheckData.StaticData.Header.StaticDataVersion = PARANDIS_DEBUG_STATIC_DATA_VERSION;
BugCheckData.StaticData.Header.PerNicDataVersion = PARANDIS_DEBUG_PER_NIC_DATA_VERSION;
BugCheckData.StaticData.Header.ulMaxContexts = MAX_CONTEXTS;
BugCheckData.StaticData.Header.SizeOfPointer = sizeof(PVOID);
BugCheckData.StaticData.Header.PerNicData = (UINT_PTR)(PVOID)BugCheckData.StaticData.PerNicData;
BugCheckData.StaticData.Header.DataArea = (UINT64)&BugCheckData.StaticData.Data;
BugCheckData.StaticData.Header.DataAreaSize = sizeof(BugCheckData.StaticData.Data);
BugCheckData.StaticData.Data.StaticDataV0.HistoryDataVersion = PARANDIS_DEBUG_HISTORY_DATA_VERSION;
BugCheckData.StaticData.Data.StaticDataV0.SizeOfHistory = MAX_HISTORY;
BugCheckData.StaticData.Data.StaticDataV0.SizeOfHistoryEntry = sizeof(tBugCheckHistoryDataEntry);
BugCheckData.StaticData.Data.StaticDataV0.HistoryData = (UINT_PTR)(PVOID)BugCheckData.StaticData.History;
BugCheckData.StaticData.Data.PendingNblEntryVersion = PARANDIS_DEBUG_PENDING_NBL_ENTRY_VERSION;
BugCheckData.StaticData.Data.PendingNblData = (UINT_PTR)(PVOID)BugCheckData.StaticData.PendingNbls;
BugCheckData.StaticData.Data.MaxPendingNbl = MAX_KEEP_NBLS;
BugCheckData.Location.Address = (UINT64)&BugCheckData;
BugCheckData.Location.Size = sizeof(BugCheckData);
RtlInitializeBitMap(&BugCheckData.StaticData.PendingNblsBitmap, BugCheckData.StaticData.PendingNblsBitmapBuffer, MAX_KEEP_NBLS);
}
void ParaNdis_DebugRegisterMiniport(PARANDIS_ADAPTER *pContext, BOOLEAN bRegister)
{
UINT i;
NdisAcquireSpinLock(&CrashLock);
for (i = 0; i < MAX_CONTEXTS; ++i)
{
UINT64 val1 = bRegister ? 0 : (UINT_PTR)pContext;
UINT64 val2 = bRegister ? (UINT_PTR)pContext : 0;
if (BugCheckData.StaticData.PerNicData[i].Context != val1) continue;
BugCheckData.StaticData.PerNicData[i].Context = val2;
break;
}
NdisReleaseSpinLock(&CrashLock);
}
#if ENABLE_CRASH_CALLBACK
static UINT FillDataOnBugCheck()
{
UINT i, n = 0;
NdisGetCurrentSystemTime(&BugCheckData.StaticData.Header.qCrashTime);
for (i = 0; i < MAX_CONTEXTS; ++i)
{
tBugCheckPerNicDataContent *pSave = &BugCheckData.StaticData.PerNicData[i];
PARANDIS_ADAPTER *p = (PARANDIS_ADAPTER *)(UINT_PTR)pSave->Context;
if (!p) continue;
pSave->nofReadyTxBuffers = 0;
for (UINT j = 0; j < p->nPathBundles; j++)
{
pSave->nofReadyTxBuffers += p->pPathBundles[j].txPath.GetFreeHWBuffers();
}
pSave->LastInterruptTimeStamp.QuadPart = PARANDIS_GET_LAST_INTERRUPT_TIMESTAMP(p);
pSave->LastTxCompletionTimeStamp = p->LastTxCompletionTimeStamp;
ParaNdis_CallOnBugCheck(p);
++n;
}
return n;
}
VOID ParaNdis_OnBugCheck(
IN KBUGCHECK_CALLBACK_REASON Reason,
IN PKBUGCHECK_REASON_CALLBACK_RECORD Record,
IN OUT PVOID ReasonSpecificData,
IN ULONG ReasonSpecificDataLength
)
{
KBUGCHECK_SECONDARY_DUMP_DATA *pDump = (KBUGCHECK_SECONDARY_DUMP_DATA *)ReasonSpecificData;
UNREFERENCED_PARAMETER(Record);
if (KbCallbackSecondaryDumpData == Reason && ReasonSpecificDataLength >= sizeof(*pDump))
{
ULONG dumpSize = sizeof(BugCheckData.Location);
if (!pDump->OutBuffer)
{
UINT nSaved;
nSaved = FillDataOnBugCheck();
if (pDump->InBufferLength >= dumpSize)
{
pDump->OutBuffer = pDump->InBuffer;
pDump->OutBufferLength = dumpSize;
}
else
{
pDump->OutBuffer = &BugCheckData.Location;
pDump->OutBufferLength = dumpSize;
bNative = FALSE;
}
DPrintf(0, "[%s] system buffer of %d, saving data for %d NIC\n", __FUNCTION__,pDump->InBufferLength, nSaved);
DPrintf(0, "[%s] using %s buffer\n", __FUNCTION__, bNative ? "native" : "own");
}
else if (pDump->OutBuffer == pDump->InBuffer)
{
RtlCopyMemory(&pDump->Guid, &ParaNdis_CrashGuid, sizeof(pDump->Guid));
RtlCopyMemory(pDump->InBuffer, &BugCheckData.Location, dumpSize);
pDump->OutBufferLength = dumpSize;
DPrintf(0, "[%s] written %d to 0x%llx\n", __FUNCTION__, (ULONG)BugCheckData.Location.Size, (UINT_PTR)BugCheckData.Location.Address );
DPrintf(0, "[%s] dump data (%d) at %p\n", __FUNCTION__, pDump->OutBufferLength, pDump->OutBuffer);
}
}
}
#endif
#if defined(ENABLE_HISTORY_LOG)
void ParaNdis_DebugHistory(
PARANDIS_ADAPTER *pContext,
eHistoryLogOperation op,
PVOID pParam1,
ULONG lParam2,
ULONG lParam3,
ULONG lParam4)
{
tBugCheckHistoryDataEntry *phe;
ULONG index = InterlockedIncrement(&BugCheckData.StaticData.Data.CurrentHistoryIndex);
index = (index - 1) % MAX_HISTORY;
phe = &BugCheckData.StaticData.History[index];
phe->Context = (UINT_PTR)pContext;
phe->operation = op;
phe->pParam1 = (UINT_PTR)pParam1;
phe->lParam2 = lParam2;
phe->lParam3 = lParam3;
phe->lParam4 = lParam4;
#if (PARANDIS_DEBUG_HISTORY_DATA_VERSION == 1)
phe->uIRQL = KeGetCurrentIrql();
phe->uProcessor = KeGetCurrentProcessorNumber();
#endif
NdisGetCurrentSystemTime(&phe->TimeStamp);
}
#endif
#if defined(KEEP_PENDING_NBL)
void ParaNdis_DebugNBLIn(PNET_BUFFER_LIST nbl, ULONG& index)
{
NdisAcquireSpinLock(&CrashLock);
index = RtlFindClearBitsAndSet(&BugCheckData.StaticData.PendingNblsBitmap, 1, 0);
NdisReleaseSpinLock(&CrashLock);
if (index < MAX_KEEP_NBLS)
{
BugCheckData.StaticData.PendingNbls[index].NBL = (ULONG_PTR)(PVOID)nbl;
NdisGetCurrentSystemTime(&BugCheckData.StaticData.PendingNbls[index].TimeStamp);
}
else
{
// if no free bit in bitmap, ULONG(-1) returned
BugCheckData.StaticData.Data.fNBLOverflow = 1;
}
}
void ParaNdis_DebugNBLOut(ULONG index, PNET_BUFFER_LIST nbl)
{
if (index >= MAX_KEEP_NBLS)
return;
NdisAcquireSpinLock(&CrashLock);
if (!RtlCheckBit(&BugCheckData.StaticData.PendingNblsBitmap, index))
{
// simple double free
RtlAssert(__FUNCTION__, __FILE__, __LINE__, NULL);
}
else if (BugCheckData.StaticData.PendingNbls[index].NBL != (ULONG_PTR)(PVOID)nbl)
{
// complicated double free
RtlAssert(__FUNCTION__, __FILE__, __LINE__, NULL);
}
else
{
RtlClearBit(&BugCheckData.StaticData.PendingNblsBitmap, index);
BugCheckData.StaticData.PendingNbls[index].NBL = 0;
}
NdisReleaseSpinLock(&CrashLock);
}
#endif