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 (NTAPI *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 (NTAPI *KeDeregisterBugCheckReasonCallbackType) (
62     __inout PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
63     );
64 
65 typedef ULONG (NTAPI *vDbgPrintExType)(
66     __in ULONG ComponentId,
67     __in ULONG Level,
68     __in PCCH Format,
69     __in va_list arglist
70     );
71 
DummyPrintProcedure(__in ULONG ComponentId,__in ULONG Level,__in PCCH Format,__in va_list arglist)72 static ULONG NTAPI DummyPrintProcedure(
73     __in ULONG ComponentId,
74     __in ULONG Level,
75     __in PCCH Format,
76     __in va_list arglist
77     )
78 {
79     return 0;
80 }
KeRegisterBugCheckReasonCallbackDummyProc(__out PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,__in PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,__in KBUGCHECK_CALLBACK_REASON Reason,__in PUCHAR Component)81 static BOOLEAN NTAPI 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 
KeDeregisterBugCheckReasonCallbackDummyProc(__inout PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord)92 BOOLEAN NTAPI 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
DebugPrint(const char * fmt,...)108 static void __cdecl 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 
_LogOutEntry(int level,const char * s)153 void _LogOutEntry(int level, const char *s)
154 {
155     DPrintf(level, ("[%s]=>", s));
156 }
157 
_LogOutExitValue(int level,const char * s,ULONG value)158 void _LogOutExitValue(int level, const char *s, ULONG value)
159 {
160     DPrintf(level, ("[%s]<=0x%X", s, value));
161 }
162 
_LogOutString(int level,const char * s)163 void _LogOutString(int level, const char *s)
164 {
165     DPrintf(level, ("[%s]", s));
166 }
167 
WppEnableCallback(__in LPCGUID Guid,__in __int64 Logger,__in BOOLEAN Enable,__in ULONG Flags,__in UCHAR Level)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];
AnotherDbgBreak()188 static void AnotherDbgBreak()
189 {
190     DPrintf(0, ("Somebody tried to break into the debugger!"));
191 }
192 #endif
193 
ParaNdis_DebugInitialize(PVOID DriverObject,PVOID RegistryPath)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 
ParaNdis_DebugCleanup(PDRIVER_OBJECT pDriverObject)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 
ParaNdis_PrepareBugCheckData()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 
ParaNdis_DebugRegisterMiniport(PARANDIS_ADAPTER * pContext,BOOLEAN bRegister)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 
FillDataOnBugCheck()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 
ParaNdis_OnBugCheck(IN KBUGCHECK_CALLBACK_REASON Reason,IN PKBUGCHECK_REASON_CALLBACK_RECORD Record,IN OUT PVOID ReasonSpecificData,IN ULONG ReasonSpecificDataLength)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)
ParaNdis_DebugHistory(PARANDIS_ADAPTER * pContext,eHistoryLogOperation op,PVOID pParam1,ULONG lParam2,ULONG lParam3,ULONG lParam4)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