1 /* 2 * This file contains debug support procedures, common for NDIS5 and NDIS6 3 * 4 * Copyright (c) 2008-2017 Red Hat, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met : 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and / or other materials provided with the distribution. 14 * 3. Neither the names of the copyright holders nor the names of their contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 #include "ndis56common.h" 30 #include "stdarg.h" 31 #include "ntstrsafe.h" 32 33 //#define OVERRIDE_DEBUG_BREAK 34 35 #ifdef WPP_EVENT_TRACING 36 #include "ParaNdis-Debug.tmh" 37 #endif 38 39 int virtioDebugLevel = 1; 40 int nDebugLevel = 1; 41 int bDebugPrint = 1; 42 43 static NDIS_SPIN_LOCK CrashLock; 44 45 static KBUGCHECK_REASON_CALLBACK_ROUTINE ParaNdis_OnBugCheck; 46 static VOID NTAPI ParaNdis_OnBugCheck( 47 IN KBUGCHECK_CALLBACK_REASON Reason, 48 IN PKBUGCHECK_REASON_CALLBACK_RECORD Record, 49 IN OUT PVOID ReasonSpecificData, 50 IN ULONG ReasonSpecificDataLength 51 ); 52 static VOID ParaNdis_PrepareBugCheckData(); 53 54 typedef BOOLEAN (*KeRegisterBugCheckReasonCallbackType) ( 55 __out PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord, 56 __in PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine, 57 __in KBUGCHECK_CALLBACK_REASON Reason, 58 __in PUCHAR Component 59 ); 60 61 typedef BOOLEAN (*KeDeregisterBugCheckReasonCallbackType) ( 62 __inout PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord 63 ); 64 65 typedef ULONG (*vDbgPrintExType)( 66 __in ULONG ComponentId, 67 __in ULONG Level, 68 __in PCCH Format, 69 __in va_list arglist 70 ); 71 72 static ULONG DummyPrintProcedure( 73 __in ULONG ComponentId, 74 __in ULONG Level, 75 __in PCCH Format, 76 __in va_list arglist 77 ) 78 { 79 return 0; 80 } 81 static BOOLEAN KeRegisterBugCheckReasonCallbackDummyProc( 82 __out PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord, 83 __in PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine, 84 __in KBUGCHECK_CALLBACK_REASON Reason, 85 __in PUCHAR Component 86 ) 87 { 88 CallbackRecord->State = 0; 89 return FALSE; 90 } 91 92 BOOLEAN KeDeregisterBugCheckReasonCallbackDummyProc( 93 __inout PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord 94 ) 95 { 96 return FALSE; 97 } 98 99 static vDbgPrintExType PrintProcedure = DummyPrintProcedure; 100 static KeRegisterBugCheckReasonCallbackType BugCheckRegisterCallback = KeRegisterBugCheckReasonCallbackDummyProc; 101 static KeDeregisterBugCheckReasonCallbackType BugCheckDeregisterCallback = KeDeregisterBugCheckReasonCallbackDummyProc; 102 KBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord; 103 104 #if !defined(WPP_EVENT_TRACING) || defined(WPP_USE_BYPASS) 105 #if defined(DPFLTR_MASK) 106 107 //common case, except Win2K 108 static void DebugPrint(const char *fmt, ...) 109 { 110 va_list list; 111 va_start(list, fmt); 112 PrintProcedure(DPFLTR_DEFAULT_ID, 9 | DPFLTR_MASK, fmt, list); 113 #if defined(VIRTIO_DBG_USE_IOPORT) 114 { 115 NTSTATUS status; 116 // use this way of output only for DISPATCH_LEVEL, 117 // higher requires more protection 118 if (KeGetCurrentIrql() <= DISPATCH_LEVEL) 119 { 120 char buf[256]; 121 size_t len, i; 122 buf[0] = 0; 123 status = RtlStringCbVPrintfA(buf, sizeof(buf), fmt, list); 124 if (status == STATUS_SUCCESS) len = strlen(buf); 125 else if (status == STATUS_BUFFER_OVERFLOW) len = sizeof(buf); 126 else { memcpy(buf, "Can't print", 11); len = 11; } 127 NdisAcquireSpinLock(&CrashLock); 128 for (i = 0; i < len; ++i) 129 { 130 NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, buf[i]); 131 } 132 NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, '\n'); 133 NdisReleaseSpinLock(&CrashLock); 134 } 135 } 136 #endif 137 } 138 139 DEBUGPRINTFUNC pDebugPrint = DebugPrint; 140 DEBUGPRINTFUNC VirtioDebugPrintProc = DebugPrint; 141 142 #else //DPFLTR_MASK 143 #pragma message("DebugPrint for Win2K") 144 145 DEBUGPRINTFUNC pDebugPrint = DbgPrint; 146 DEBUGPRINTFUNC VirtioDebugPrintProc = DbgPrint; 147 148 #endif //DPFLTR_MASK 149 #endif //!defined(WPP_EVENT_TRACING) || defined(WPP_USE_BYPASS) 150 151 152 153 void _LogOutEntry(int level, const char *s) 154 { 155 DPrintf(level, ("[%s]=>", s)); 156 } 157 158 void _LogOutExitValue(int level, const char *s, ULONG value) 159 { 160 DPrintf(level, ("[%s]<=0x%X", s, value)); 161 } 162 163 void _LogOutString(int level, const char *s) 164 { 165 DPrintf(level, ("[%s]", s)); 166 } 167 168 VOID WppEnableCallback( 169 __in LPCGUID Guid, 170 __in __int64 Logger, 171 __in BOOLEAN Enable, 172 __in ULONG Flags, 173 __in UCHAR Level) 174 { 175 #if WPP_USE_BYPASS 176 DPrintfBypass(0, ("[%s] %s, flags %X, level %d", 177 __FUNCTION__, Enable ? "enabled" : "disabled", 178 Flags, (ULONG)Level)); 179 #endif 180 nDebugLevel = Level; 181 bDebugPrint = Enable; 182 } 183 184 185 #ifdef OVERRIDE_DEBUG_BREAK 186 static PUCHAR pDbgBreakPoint; 187 static UCHAR DbgBreakPointChunk[5]; 188 static void AnotherDbgBreak() 189 { 190 DPrintf(0, ("Somebody tried to break into the debugger!")); 191 } 192 #endif 193 194 void ParaNdis_DebugInitialize(PVOID DriverObject,PVOID RegistryPath) 195 { 196 NDIS_STRING usRegister, usDeregister, usPrint; 197 PVOID pr, pd; 198 BOOLEAN res; 199 WPP_INIT_TRACING(DriverObject, RegistryPath); 200 201 NdisAllocateSpinLock(&CrashLock); 202 KeInitializeCallbackRecord(&CallbackRecord); 203 ParaNdis_PrepareBugCheckData(); 204 NdisInitUnicodeString(&usPrint, L"vDbgPrintEx"); 205 NdisInitUnicodeString(&usRegister, L"KeRegisterBugCheckReasonCallback"); 206 NdisInitUnicodeString(&usDeregister, L"KeDeregisterBugCheckReasonCallback"); 207 pd = MmGetSystemRoutineAddress(&usPrint); 208 if (pd) PrintProcedure = (vDbgPrintExType)pd; 209 pr = MmGetSystemRoutineAddress(&usRegister); 210 pd = MmGetSystemRoutineAddress(&usDeregister); 211 if (pr && pd) 212 { 213 BugCheckRegisterCallback = (KeRegisterBugCheckReasonCallbackType)pr; 214 BugCheckDeregisterCallback = (KeDeregisterBugCheckReasonCallbackType)pd; 215 } 216 res = BugCheckRegisterCallback(&CallbackRecord, ParaNdis_OnBugCheck, KbCallbackSecondaryDumpData, "NetKvm"); 217 DPrintf(0, ("[%s] Crash callback %sregistered", __FUNCTION__, res ? "" : "NOT ")); 218 219 #ifdef OVERRIDE_DEBUG_BREAK 220 if (sizeof(PVOID) == sizeof(ULONG)) 221 { 222 UCHAR replace[5] = {0xe9,0,0,0,0}; 223 ULONG replacement; 224 NDIS_STRING usDbgBreakPointName; 225 NdisInitUnicodeString(&usDbgBreakPointName, L"DbgBreakPoint"); 226 pDbgBreakPoint = (PUCHAR)MmGetSystemRoutineAddress(&usDbgBreakPointName); 227 if (pDbgBreakPoint) 228 { 229 DPrintf(0, ("Replacing original BP handler at %p", pDbgBreakPoint)); 230 replacement = RtlPointerToOffset(pDbgBreakPoint + 5, AnotherDbgBreak); 231 RtlCopyMemory(replace + 1, &replacement, sizeof(replacement)); 232 RtlCopyMemory(DbgBreakPointChunk, pDbgBreakPoint, sizeof(DbgBreakPointChunk)); 233 RtlCopyMemory(pDbgBreakPoint, replace, sizeof(replace)); 234 } 235 } 236 #endif 237 } 238 239 void ParaNdis_DebugCleanup(PDRIVER_OBJECT pDriverObject) 240 { 241 #ifdef OVERRIDE_DEBUG_BREAK 242 if (sizeof(PVOID) == sizeof(ULONG) && pDbgBreakPoint) 243 { 244 DPrintf(0, ("Restoring original BP handler at %p", pDbgBreakPoint)); 245 RtlCopyMemory(pDbgBreakPoint, DbgBreakPointChunk, sizeof(DbgBreakPointChunk)); 246 } 247 #endif 248 BugCheckDeregisterCallback(&CallbackRecord); 249 WPP_CLEANUP(pDriverObject); 250 } 251 252 253 #define MAX_CONTEXTS 4 254 #if defined(ENABLE_HISTORY_LOG) 255 #define MAX_HISTORY 0x40000 256 #else 257 #define MAX_HISTORY 2 258 #endif 259 typedef struct _tagBugCheckStaticData 260 { 261 tBugCheckStaticDataHeader Header; 262 tBugCheckPerNicDataContent PerNicData[MAX_CONTEXTS]; 263 tBugCheckStaticDataContent Data; 264 tBugCheckHistoryDataEntry History[MAX_HISTORY]; 265 }tBugCheckStaticData; 266 267 268 typedef struct _tagBugCheckData 269 { 270 tBugCheckStaticData StaticData; 271 tBugCheckDataLocation Location; 272 }tBugCheckData; 273 274 static tBugCheckData BugCheckData; 275 static BOOLEAN bNative = TRUE; 276 277 VOID ParaNdis_PrepareBugCheckData() 278 { 279 BugCheckData.StaticData.Header.StaticDataVersion = PARANDIS_DEBUG_STATIC_DATA_VERSION; 280 BugCheckData.StaticData.Header.PerNicDataVersion = PARANDIS_DEBUG_PER_NIC_DATA_VERSION; 281 BugCheckData.StaticData.Header.ulMaxContexts = MAX_CONTEXTS; 282 BugCheckData.StaticData.Header.SizeOfPointer = sizeof(PVOID); 283 BugCheckData.StaticData.Header.PerNicData = (UINT_PTR)(PVOID)BugCheckData.StaticData.PerNicData; 284 BugCheckData.StaticData.Header.DataArea = (UINT64)&BugCheckData.StaticData.Data; 285 BugCheckData.StaticData.Header.DataAreaSize = sizeof(BugCheckData.StaticData.Data); 286 BugCheckData.StaticData.Data.HistoryDataVersion = PARANDIS_DEBUG_HISTORY_DATA_VERSION; 287 BugCheckData.StaticData.Data.SizeOfHistory = MAX_HISTORY; 288 BugCheckData.StaticData.Data.SizeOfHistoryEntry = sizeof(tBugCheckHistoryDataEntry); 289 BugCheckData.StaticData.Data.HistoryData = (UINT_PTR)(PVOID)BugCheckData.StaticData.History; 290 BugCheckData.Location.Address = (UINT64)&BugCheckData; 291 BugCheckData.Location.Size = sizeof(BugCheckData); 292 } 293 294 void ParaNdis_DebugRegisterMiniport(PARANDIS_ADAPTER *pContext, BOOLEAN bRegister) 295 { 296 UINT i; 297 NdisAcquireSpinLock(&CrashLock); 298 for (i = 0; i < MAX_CONTEXTS; ++i) 299 { 300 UINT64 val1 = bRegister ? 0 : (UINT_PTR)pContext; 301 UINT64 val2 = bRegister ? (UINT_PTR)pContext : 0; 302 if (BugCheckData.StaticData.PerNicData[i].Context != val1) continue; 303 BugCheckData.StaticData.PerNicData[i].Context = val2; 304 break; 305 } 306 NdisReleaseSpinLock(&CrashLock); 307 } 308 309 static UINT FillDataOnBugCheck() 310 { 311 UINT i, n = 0; 312 NdisGetCurrentSystemTime(&BugCheckData.StaticData.Header.qCrashTime); 313 for (i = 0; i < MAX_CONTEXTS; ++i) 314 { 315 tBugCheckPerNicDataContent *pSave = &BugCheckData.StaticData.PerNicData[i]; 316 PARANDIS_ADAPTER *p = (PARANDIS_ADAPTER *)pSave->Context; 317 if (!p) continue; 318 pSave->nofPacketsToComplete = p->NetTxPacketsToReturn; 319 pSave->nofReadyTxBuffers = p->nofFreeHardwareBuffers; 320 pSave->LastInterruptTimeStamp.QuadPart = PARANDIS_GET_LAST_INTERRUPT_TIMESTAMP(p); 321 pSave->LastTxCompletionTimeStamp = p->LastTxCompletionTimeStamp; 322 ParaNdis_CallOnBugCheck(p); 323 ++n; 324 } 325 return n; 326 } 327 328 VOID NTAPI ParaNdis_OnBugCheck( 329 IN KBUGCHECK_CALLBACK_REASON Reason, 330 IN PKBUGCHECK_REASON_CALLBACK_RECORD Record, 331 IN OUT PVOID ReasonSpecificData, 332 IN ULONG ReasonSpecificDataLength 333 ) 334 { 335 KBUGCHECK_SECONDARY_DUMP_DATA *pDump = (KBUGCHECK_SECONDARY_DUMP_DATA *)ReasonSpecificData; 336 if (KbCallbackSecondaryDumpData == Reason && ReasonSpecificDataLength >= sizeof(*pDump)) 337 { 338 ULONG dumpSize = sizeof(BugCheckData.Location); 339 if (!pDump->OutBuffer) 340 { 341 UINT nSaved; 342 nSaved = FillDataOnBugCheck(); 343 if (pDump->InBufferLength >= dumpSize) 344 { 345 pDump->OutBuffer = pDump->InBuffer; 346 pDump->OutBufferLength = dumpSize; 347 } 348 else 349 { 350 pDump->OutBuffer = &BugCheckData.Location; 351 pDump->OutBufferLength = dumpSize; 352 bNative = FALSE; 353 } 354 DPrintf(0, ("[%s] system buffer of %d, saving data for %d NIC", __FUNCTION__,pDump->InBufferLength, nSaved)); 355 DPrintf(0, ("[%s] using %s buffer", __FUNCTION__, bNative ? "native" : "own")); 356 } 357 else if (pDump->OutBuffer == pDump->InBuffer) 358 { 359 RtlCopyMemory(&pDump->Guid, &ParaNdis_CrashGuid, sizeof(pDump->Guid)); 360 RtlCopyMemory(pDump->InBuffer, &BugCheckData.Location, dumpSize); 361 pDump->OutBufferLength = dumpSize; 362 DPrintf(0, ("[%s] written %d to %p", __FUNCTION__, (ULONG)BugCheckData.Location.Size, (UINT_PTR)BugCheckData.Location.Address )); 363 DPrintf(0, ("[%s] dump data (%d) at %p", __FUNCTION__, pDump->OutBufferLength, pDump->OutBuffer)); 364 } 365 } 366 } 367 368 #if defined(ENABLE_HISTORY_LOG) 369 void ParaNdis_DebugHistory( 370 PARANDIS_ADAPTER *pContext, 371 eHistoryLogOperation op, 372 PVOID pParam1, 373 ULONG lParam2, 374 ULONG lParam3, 375 ULONG lParam4) 376 { 377 tBugCheckHistoryDataEntry *phe; 378 ULONG index = InterlockedIncrement(&BugCheckData.StaticData.Data.CurrentHistoryIndex); 379 index = (index - 1) % MAX_HISTORY; 380 phe = &BugCheckData.StaticData.History[index]; 381 phe->Context = (UINT_PTR)pContext; 382 phe->operation = op; 383 phe->pParam1 = (UINT_PTR)pParam1; 384 phe->lParam2 = lParam2; 385 phe->lParam3 = lParam3; 386 phe->lParam4 = lParam4; 387 #if (PARANDIS_DEBUG_HISTORY_DATA_VERSION == 1) 388 phe->uIRQL = KeGetCurrentIrql(); 389 phe->uProcessor = KeGetCurrentProcessorNumber(); 390 #endif 391 NdisGetCurrentSystemTime(&phe->TimeStamp); 392 } 393 394 #endif 395