1*8a978a17SVictor Perevertkin /*++
2*8a978a17SVictor Perevertkin 
3*8a978a17SVictor Perevertkin Copyright (c) Microsoft Corporation.  All rights reserved.
4*8a978a17SVictor Perevertkin 
5*8a978a17SVictor Perevertkin Module Name:
6*8a978a17SVictor Perevertkin 
7*8a978a17SVictor Perevertkin     Verifier.cpp
8*8a978a17SVictor Perevertkin 
9*8a978a17SVictor Perevertkin Abstract:
10*8a978a17SVictor Perevertkin 
11*8a978a17SVictor Perevertkin     This file has implementation of verifier support routines
12*8a978a17SVictor Perevertkin 
13*8a978a17SVictor Perevertkin Author:
14*8a978a17SVictor Perevertkin 
15*8a978a17SVictor Perevertkin 
16*8a978a17SVictor Perevertkin 
17*8a978a17SVictor Perevertkin Environment:
18*8a978a17SVictor Perevertkin 
19*8a978a17SVictor Perevertkin     Shared (Kernel and user)
20*8a978a17SVictor Perevertkin 
21*8a978a17SVictor Perevertkin Revision History:
22*8a978a17SVictor Perevertkin 
23*8a978a17SVictor Perevertkin 
24*8a978a17SVictor Perevertkin 
25*8a978a17SVictor Perevertkin --*/
26*8a978a17SVictor Perevertkin 
27*8a978a17SVictor Perevertkin 
28*8a978a17SVictor Perevertkin #include "vfpriv.hpp"
29*8a978a17SVictor Perevertkin 
30*8a978a17SVictor Perevertkin extern "C"
31*8a978a17SVictor Perevertkin {
32*8a978a17SVictor Perevertkin 
33*8a978a17SVictor Perevertkin extern WDFVERSION WdfVersion;
34*8a978a17SVictor Perevertkin 
35*8a978a17SVictor Perevertkin // Tracing support
36*8a978a17SVictor Perevertkin #if defined(EVENT_TRACING)
37*8a978a17SVictor Perevertkin #include "verifier.tmh"
38*8a978a17SVictor Perevertkin #endif
39*8a978a17SVictor Perevertkin 
40*8a978a17SVictor Perevertkin #ifdef  ALLOC_PRAGMA
41*8a978a17SVictor Perevertkin #pragma alloc_text(FX_ENHANCED_VERIFIER_SECTION_NAME,  \
42*8a978a17SVictor Perevertkin     AddEventHooksWdfDeviceCreate,                      \
43*8a978a17SVictor Perevertkin     AddEventHooksWdfIoQueueCreate,                     \
44*8a978a17SVictor Perevertkin     VfAddContextToHandle,                              \
45*8a978a17SVictor Perevertkin     VfAllocateContext,                                 \
46*8a978a17SVictor Perevertkin     VfWdfObjectGetTypedContext                        \
47*8a978a17SVictor Perevertkin     )
48*8a978a17SVictor Perevertkin #endif
49*8a978a17SVictor Perevertkin 
50*8a978a17SVictor Perevertkin _Must_inspect_result_
51*8a978a17SVictor Perevertkin NTSTATUS
AddEventHooksWdfDeviceCreate(__inout PVF_HOOK_PROCESS_INFO HookProcessInfo,__in PWDF_DRIVER_GLOBALS DriverGlobals,__in PWDFDEVICE_INIT * DeviceInit,__in PWDF_OBJECT_ATTRIBUTES DeviceAttributes,__out WDFDEVICE * Device)52*8a978a17SVictor Perevertkin AddEventHooksWdfDeviceCreate(
53*8a978a17SVictor Perevertkin     __inout PVF_HOOK_PROCESS_INFO HookProcessInfo,
54*8a978a17SVictor Perevertkin     __in PWDF_DRIVER_GLOBALS DriverGlobals,
55*8a978a17SVictor Perevertkin     __in PWDFDEVICE_INIT* DeviceInit,
56*8a978a17SVictor Perevertkin     __in PWDF_OBJECT_ATTRIBUTES DeviceAttributes,
57*8a978a17SVictor Perevertkin     __out WDFDEVICE* Device
58*8a978a17SVictor Perevertkin     )
59*8a978a17SVictor Perevertkin /*++
60*8a978a17SVictor Perevertkin 
61*8a978a17SVictor Perevertkin Routine Description:
62*8a978a17SVictor Perevertkin 
63*8a978a17SVictor Perevertkin     This routine is called by main hook for WdfDeviceCreate.
64*8a978a17SVictor Perevertkin     The routine swaps the event callbacks provided by client.
65*8a978a17SVictor Perevertkin 
66*8a978a17SVictor Perevertkin Arguments:
67*8a978a17SVictor Perevertkin 
68*8a978a17SVictor Perevertkin Return value:
69*8a978a17SVictor Perevertkin 
70*8a978a17SVictor Perevertkin --*/
71*8a978a17SVictor Perevertkin {
72*8a978a17SVictor Perevertkin     NTSTATUS status;
73*8a978a17SVictor Perevertkin     PWDFDEVICE_INIT deviceInit = *DeviceInit;
74*8a978a17SVictor Perevertkin     WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerEvtsOriginal;
75*8a978a17SVictor Perevertkin     WDF_PNPPOWER_EVENT_CALLBACKS *pnpPowerEvts;
76*8a978a17SVictor Perevertkin     PFX_DRIVER_GLOBALS pFxDriverGlobals;
77*8a978a17SVictor Perevertkin     PVOID contextHeader = NULL;
78*8a978a17SVictor Perevertkin     WDF_OBJECT_ATTRIBUTES  attributes;
79*8a978a17SVictor Perevertkin 
80*8a978a17SVictor Perevertkin     PAGED_CODE_LOCKED();
81*8a978a17SVictor Perevertkin 
82*8a978a17SVictor Perevertkin     pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals);
83*8a978a17SVictor Perevertkin 
84*8a978a17SVictor Perevertkin     FxPointerNotNull(pFxDriverGlobals, DeviceInit);
85*8a978a17SVictor Perevertkin     FxPointerNotNull(pFxDriverGlobals, *DeviceInit);
86*8a978a17SVictor Perevertkin     FxPointerNotNull(pFxDriverGlobals, Device);
87*8a978a17SVictor Perevertkin 
88*8a978a17SVictor Perevertkin     //
89*8a978a17SVictor Perevertkin     // Check if there are any callbacks set by the client driver. If not, we
90*8a978a17SVictor Perevertkin     // don't need any callback hooking.
91*8a978a17SVictor Perevertkin     //
92*8a978a17SVictor Perevertkin     if (deviceInit->PnpPower.PnpPowerEventCallbacks.Size !=
93*8a978a17SVictor Perevertkin         sizeof(WDF_PNPPOWER_EVENT_CALLBACKS)) {
94*8a978a17SVictor Perevertkin         //
95*8a978a17SVictor Perevertkin         // no hooking required.
96*8a978a17SVictor Perevertkin         //
97*8a978a17SVictor Perevertkin         status = STATUS_SUCCESS;
98*8a978a17SVictor Perevertkin         HookProcessInfo->DonotCallKmdfLib = FALSE;
99*8a978a17SVictor Perevertkin         return status;
100*8a978a17SVictor Perevertkin     }
101*8a978a17SVictor Perevertkin 
102*8a978a17SVictor Perevertkin     //
103*8a978a17SVictor Perevertkin     // Hooking can be done only if we are able to allocate context memory.
104*8a978a17SVictor Perevertkin     // Try to allocate a context
105*8a978a17SVictor Perevertkin     //
106*8a978a17SVictor Perevertkin     WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
107*8a978a17SVictor Perevertkin                                             &attributes,
108*8a978a17SVictor Perevertkin                                             VF_WDFDEVICECREATE_CONTEXT
109*8a978a17SVictor Perevertkin                                             );
110*8a978a17SVictor Perevertkin 
111*8a978a17SVictor Perevertkin     status = VfAllocateContext(DriverGlobals, &attributes, &contextHeader);
112*8a978a17SVictor Perevertkin 
113*8a978a17SVictor Perevertkin     if (!NT_SUCCESS(status)) {
114*8a978a17SVictor Perevertkin         //
115*8a978a17SVictor Perevertkin         // couldn't allocate context. hooking not possible
116*8a978a17SVictor Perevertkin         //
117*8a978a17SVictor Perevertkin         HookProcessInfo->DonotCallKmdfLib = FALSE;
118*8a978a17SVictor Perevertkin         return status;
119*8a978a17SVictor Perevertkin     }
120*8a978a17SVictor Perevertkin 
121*8a978a17SVictor Perevertkin     //
122*8a978a17SVictor Perevertkin     // store original driver callbacks to local variable
123*8a978a17SVictor Perevertkin     //
124*8a978a17SVictor Perevertkin     RtlCopyMemory(&pnpPowerEvtsOriginal,
125*8a978a17SVictor Perevertkin         &deviceInit->PnpPower.PnpPowerEventCallbacks,
126*8a978a17SVictor Perevertkin         sizeof(WDF_PNPPOWER_EVENT_CALLBACKS)
127*8a978a17SVictor Perevertkin         );
128*8a978a17SVictor Perevertkin 
129*8a978a17SVictor Perevertkin     //
130*8a978a17SVictor Perevertkin     // Set callback hooks
131*8a978a17SVictor Perevertkin     //
132*8a978a17SVictor Perevertkin     // Normally override the hooks on local copy of stucture (not the original
133*8a978a17SVictor Perevertkin     // structure itself) and then pass the local struture to DDI call and
134*8a978a17SVictor Perevertkin     // copy back the original poniter when returning back to caller. In this case
135*8a978a17SVictor Perevertkin     // device_init is null'ed out by fx when returning to caller so we can
136*8a978a17SVictor Perevertkin     // directly override the original
137*8a978a17SVictor Perevertkin     //
138*8a978a17SVictor Perevertkin     pnpPowerEvts = &deviceInit->PnpPower.PnpPowerEventCallbacks;
139*8a978a17SVictor Perevertkin 
140*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0Entry);
141*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0EntryPostInterruptsEnabled);
142*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0Exit);
143*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0ExitPreInterruptsDisabled);
144*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDevicePrepareHardware);
145*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceReleaseHardware);
146*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoCleanup);
147*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoFlush);
148*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoInit);
149*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoSuspend);
150*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoRestart);
151*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSurpriseRemoval);
152*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceQueryRemove);
153*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceQueryStop);
154*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceUsageNotification);
155*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceUsageNotificationEx);
156*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceRelationsQuery);
157*8a978a17SVictor Perevertkin 
158*8a978a17SVictor Perevertkin     //
159*8a978a17SVictor Perevertkin     // Call the DDI on behalf of driver.
160*8a978a17SVictor Perevertkin     //
161*8a978a17SVictor Perevertkin     status = ((PFN_WDFDEVICECREATE)WdfVersion.Functions.pfnWdfDeviceCreate)(
162*8a978a17SVictor Perevertkin                 DriverGlobals,
163*8a978a17SVictor Perevertkin                 DeviceInit,
164*8a978a17SVictor Perevertkin                 DeviceAttributes,
165*8a978a17SVictor Perevertkin                 Device
166*8a978a17SVictor Perevertkin                 );
167*8a978a17SVictor Perevertkin     //
168*8a978a17SVictor Perevertkin     // Tell main hook routine not to call the DDI in kmdf lib since we
169*8a978a17SVictor Perevertkin     // already called it
170*8a978a17SVictor Perevertkin     //
171*8a978a17SVictor Perevertkin     HookProcessInfo->DonotCallKmdfLib = TRUE;
172*8a978a17SVictor Perevertkin     HookProcessInfo->DdiCallStatus = status;
173*8a978a17SVictor Perevertkin 
174*8a978a17SVictor Perevertkin     //
175*8a978a17SVictor Perevertkin     // if DDI Succeeds, add context
176*8a978a17SVictor Perevertkin     //
177*8a978a17SVictor Perevertkin     if (NT_SUCCESS(status)) {
178*8a978a17SVictor Perevertkin         PVF_WDFDEVICECREATE_CONTEXT context = NULL;
179*8a978a17SVictor Perevertkin 
180*8a978a17SVictor Perevertkin         //
181*8a978a17SVictor Perevertkin         // add the already allocated context to the object.
182*8a978a17SVictor Perevertkin         //
183*8a978a17SVictor Perevertkin         status = VfAddContextToHandle(contextHeader,
184*8a978a17SVictor Perevertkin                                       &attributes,
185*8a978a17SVictor Perevertkin                                       *Device,
186*8a978a17SVictor Perevertkin                                       (PVOID *)&context);
187*8a978a17SVictor Perevertkin 
188*8a978a17SVictor Perevertkin         if (NT_SUCCESS(status)) {
189*8a978a17SVictor Perevertkin             //
190*8a978a17SVictor Perevertkin             // store the DriverGlobals pointer used to associate the driver
191*8a978a17SVictor Perevertkin             // with its client driver callback later in the callback hooks
192*8a978a17SVictor Perevertkin             //
193*8a978a17SVictor Perevertkin             context->CommonHeader.DriverGlobals = DriverGlobals;
194*8a978a17SVictor Perevertkin 
195*8a978a17SVictor Perevertkin             //
196*8a978a17SVictor Perevertkin             // store original client driver callbacks in context
197*8a978a17SVictor Perevertkin             //
198*8a978a17SVictor Perevertkin             RtlCopyMemory(
199*8a978a17SVictor Perevertkin                 &context->PnpPowerEventCallbacksOriginal,
200*8a978a17SVictor Perevertkin                 &pnpPowerEvtsOriginal,
201*8a978a17SVictor Perevertkin                 sizeof(WDF_PNPPOWER_EVENT_CALLBACKS)
202*8a978a17SVictor Perevertkin                 );
203*8a978a17SVictor Perevertkin         }
204*8a978a17SVictor Perevertkin         else {
205*8a978a17SVictor Perevertkin             //
206*8a978a17SVictor Perevertkin             // For some reason adding context to handle failed. This should be
207*8a978a17SVictor Perevertkin             // rare failure, because context allocation was already successful,
208*8a978a17SVictor Perevertkin             // only adding to handle failed.
209*8a978a17SVictor Perevertkin             //
210*8a978a17SVictor Perevertkin             // Hooking failed if adding context is unsuccessful. This means
211*8a978a17SVictor Perevertkin             // kmdf has callbacks hooks but verifier cannot assiociate client
212*8a978a17SVictor Perevertkin             // driver callbacks with callback hooks. This would be a fatal error.
213*8a978a17SVictor Perevertkin             //
214*8a978a17SVictor Perevertkin             ASSERTMSG("KMDF Enhanced Verifier failed to add context to object "
215*8a978a17SVictor Perevertkin                 "handle\n", FALSE);
216*8a978a17SVictor Perevertkin 
217*8a978a17SVictor Perevertkin             if (contextHeader != NULL) {
218*8a978a17SVictor Perevertkin                 FxPoolFree(contextHeader);
219*8a978a17SVictor Perevertkin             }
220*8a978a17SVictor Perevertkin         }
221*8a978a17SVictor Perevertkin     }
222*8a978a17SVictor Perevertkin     else {
223*8a978a17SVictor Perevertkin         //
224*8a978a17SVictor Perevertkin         // KMDF DDI call failed. In case of failure, DeviceInit is not NULL'ed
225*8a978a17SVictor Perevertkin         // so the driver could potentially call WdfDeviceCreate again with this
226*8a978a17SVictor Perevertkin         // DeviceInit that contains callback hooks, and result in infinite
227*8a978a17SVictor Perevertkin         // callback loop. So put original callbacks back
228*8a978a17SVictor Perevertkin         //
229*8a978a17SVictor Perevertkin         if ((*DeviceInit) != NULL) {
230*8a978a17SVictor Perevertkin             //
231*8a978a17SVictor Perevertkin             // we overwrote only the pnppower callbacks. Put the original back.
232*8a978a17SVictor Perevertkin             //
233*8a978a17SVictor Perevertkin             deviceInit = *DeviceInit;
234*8a978a17SVictor Perevertkin             RtlCopyMemory(&deviceInit->PnpPower.PnpPowerEventCallbacks,
235*8a978a17SVictor Perevertkin                 &pnpPowerEvtsOriginal,
236*8a978a17SVictor Perevertkin                 sizeof(WDF_PNPPOWER_EVENT_CALLBACKS)
237*8a978a17SVictor Perevertkin                 );
238*8a978a17SVictor Perevertkin         }
239*8a978a17SVictor Perevertkin 
240*8a978a17SVictor Perevertkin         if (contextHeader != NULL) {
241*8a978a17SVictor Perevertkin             FxPoolFree(contextHeader);
242*8a978a17SVictor Perevertkin         }
243*8a978a17SVictor Perevertkin     }
244*8a978a17SVictor Perevertkin 
245*8a978a17SVictor Perevertkin     return status;
246*8a978a17SVictor Perevertkin }
247*8a978a17SVictor Perevertkin 
248*8a978a17SVictor Perevertkin _Must_inspect_result_
249*8a978a17SVictor Perevertkin NTSTATUS
AddEventHooksWdfIoQueueCreate(__inout PVF_HOOK_PROCESS_INFO HookProcessInfo,__in PWDF_DRIVER_GLOBALS DriverGlobals,__in WDFDEVICE Device,__in PWDF_IO_QUEUE_CONFIG Config,__in PWDF_OBJECT_ATTRIBUTES QueueAttributes,__out WDFQUEUE * Queue)250*8a978a17SVictor Perevertkin AddEventHooksWdfIoQueueCreate(
251*8a978a17SVictor Perevertkin     __inout PVF_HOOK_PROCESS_INFO HookProcessInfo,
252*8a978a17SVictor Perevertkin     __in PWDF_DRIVER_GLOBALS DriverGlobals,
253*8a978a17SVictor Perevertkin     __in WDFDEVICE Device,
254*8a978a17SVictor Perevertkin     __in PWDF_IO_QUEUE_CONFIG Config,
255*8a978a17SVictor Perevertkin     __in PWDF_OBJECT_ATTRIBUTES QueueAttributes,
256*8a978a17SVictor Perevertkin     __out WDFQUEUE* Queue
257*8a978a17SVictor Perevertkin     )
258*8a978a17SVictor Perevertkin /*++
259*8a978a17SVictor Perevertkin 
260*8a978a17SVictor Perevertkin Routine Description:
261*8a978a17SVictor Perevertkin 
262*8a978a17SVictor Perevertkin Arguments:
263*8a978a17SVictor Perevertkin 
264*8a978a17SVictor Perevertkin Return value:
265*8a978a17SVictor Perevertkin 
266*8a978a17SVictor Perevertkin --*/
267*8a978a17SVictor Perevertkin {
268*8a978a17SVictor Perevertkin     NTSTATUS status;
269*8a978a17SVictor Perevertkin     WDF_IO_QUEUE_CONFIG  configNew;
270*8a978a17SVictor Perevertkin     PFX_DRIVER_GLOBALS pFxDriverGlobals;
271*8a978a17SVictor Perevertkin     WDFQUEUE *pQueue;
272*8a978a17SVictor Perevertkin     WDFQUEUE queue;
273*8a978a17SVictor Perevertkin     PVOID contextHeader = NULL;
274*8a978a17SVictor Perevertkin     WDF_OBJECT_ATTRIBUTES  attributes;
275*8a978a17SVictor Perevertkin 
276*8a978a17SVictor Perevertkin     PAGED_CODE_LOCKED();
277*8a978a17SVictor Perevertkin 
278*8a978a17SVictor Perevertkin     pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals);
279*8a978a17SVictor Perevertkin 
280*8a978a17SVictor Perevertkin     FxPointerNotNull(pFxDriverGlobals, Config);
281*8a978a17SVictor Perevertkin 
282*8a978a17SVictor Perevertkin     //
283*8a978a17SVictor Perevertkin     // Check if there are any callbacks set by the client driver. If not, we
284*8a978a17SVictor Perevertkin     // don't need any callback hooking.
285*8a978a17SVictor Perevertkin     //
286*8a978a17SVictor Perevertkin     if (Config->Size != sizeof(WDF_IO_QUEUE_CONFIG)) {
287*8a978a17SVictor Perevertkin         //
288*8a978a17SVictor Perevertkin         // no hooking required.
289*8a978a17SVictor Perevertkin         //
290*8a978a17SVictor Perevertkin         status = STATUS_SUCCESS;
291*8a978a17SVictor Perevertkin         HookProcessInfo->DonotCallKmdfLib = FALSE;
292*8a978a17SVictor Perevertkin         return status;
293*8a978a17SVictor Perevertkin     }
294*8a978a17SVictor Perevertkin 
295*8a978a17SVictor Perevertkin     //
296*8a978a17SVictor Perevertkin     // Hooking can be done only if we are able to allocate context memory.
297*8a978a17SVictor Perevertkin     // Try to allocate a context
298*8a978a17SVictor Perevertkin     //
299*8a978a17SVictor Perevertkin     WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
300*8a978a17SVictor Perevertkin                                             VF_WDFIOQUEUECREATE_CONTEXT);
301*8a978a17SVictor Perevertkin 
302*8a978a17SVictor Perevertkin     status = VfAllocateContext(DriverGlobals, &attributes, &contextHeader);
303*8a978a17SVictor Perevertkin 
304*8a978a17SVictor Perevertkin     if (!NT_SUCCESS(status)) {
305*8a978a17SVictor Perevertkin         //
306*8a978a17SVictor Perevertkin         // couldn't allocate context. hooking not possible
307*8a978a17SVictor Perevertkin         //
308*8a978a17SVictor Perevertkin         HookProcessInfo->DonotCallKmdfLib = FALSE;
309*8a978a17SVictor Perevertkin         return status;
310*8a978a17SVictor Perevertkin     }
311*8a978a17SVictor Perevertkin 
312*8a978a17SVictor Perevertkin     //
313*8a978a17SVictor Perevertkin     // create a local copy of config
314*8a978a17SVictor Perevertkin     //
315*8a978a17SVictor Perevertkin     RtlCopyMemory(&configNew,
316*8a978a17SVictor Perevertkin         Config,
317*8a978a17SVictor Perevertkin         sizeof(configNew)
318*8a978a17SVictor Perevertkin         );
319*8a978a17SVictor Perevertkin 
320*8a978a17SVictor Perevertkin     //
321*8a978a17SVictor Perevertkin     // Override local copy with event callback hooks.
322*8a978a17SVictor Perevertkin     //
323*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoDefault);
324*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoRead);
325*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoWrite);
326*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoDeviceControl);
327*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoInternalDeviceControl);
328*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoStop);
329*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoResume);
330*8a978a17SVictor Perevertkin     SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoCanceledOnQueue);
331*8a978a17SVictor Perevertkin 
332*8a978a17SVictor Perevertkin     //
333*8a978a17SVictor Perevertkin     // Queue handle is an optional parameter
334*8a978a17SVictor Perevertkin     //
335*8a978a17SVictor Perevertkin     if (Queue == NULL) {
336*8a978a17SVictor Perevertkin         pQueue = &queue;
337*8a978a17SVictor Perevertkin     }
338*8a978a17SVictor Perevertkin     else {
339*8a978a17SVictor Perevertkin         pQueue = Queue;
340*8a978a17SVictor Perevertkin     }
341*8a978a17SVictor Perevertkin 
342*8a978a17SVictor Perevertkin     //
343*8a978a17SVictor Perevertkin     // call the DDI
344*8a978a17SVictor Perevertkin     //
345*8a978a17SVictor Perevertkin     status = WdfVersion.Functions.pfnWdfIoQueueCreate(
346*8a978a17SVictor Perevertkin                 DriverGlobals,
347*8a978a17SVictor Perevertkin                 Device,
348*8a978a17SVictor Perevertkin                 &configNew,
349*8a978a17SVictor Perevertkin                 QueueAttributes,
350*8a978a17SVictor Perevertkin                 pQueue
351*8a978a17SVictor Perevertkin                 );
352*8a978a17SVictor Perevertkin 
353*8a978a17SVictor Perevertkin     //
354*8a978a17SVictor Perevertkin     // Tell main hook routine not to call the DDI in kmdf lib since we
355*8a978a17SVictor Perevertkin     // already called it
356*8a978a17SVictor Perevertkin     //
357*8a978a17SVictor Perevertkin     HookProcessInfo->DonotCallKmdfLib = TRUE;
358*8a978a17SVictor Perevertkin     HookProcessInfo->DdiCallStatus = status;
359*8a978a17SVictor Perevertkin 
360*8a978a17SVictor Perevertkin     //
361*8a978a17SVictor Perevertkin     // if DDI Succeeds, add context
362*8a978a17SVictor Perevertkin     //
363*8a978a17SVictor Perevertkin     if (NT_SUCCESS(status)) {
364*8a978a17SVictor Perevertkin         PVF_WDFIOQUEUECREATE_CONTEXT  context = NULL;
365*8a978a17SVictor Perevertkin 
366*8a978a17SVictor Perevertkin         //
367*8a978a17SVictor Perevertkin         // add the already allocated context to the object.
368*8a978a17SVictor Perevertkin         //
369*8a978a17SVictor Perevertkin         status = VfAddContextToHandle(contextHeader,
370*8a978a17SVictor Perevertkin                                       &attributes,
371*8a978a17SVictor Perevertkin                                       *pQueue,
372*8a978a17SVictor Perevertkin                                       (PVOID *)&context);
373*8a978a17SVictor Perevertkin 
374*8a978a17SVictor Perevertkin         if (NT_SUCCESS(status)) {
375*8a978a17SVictor Perevertkin             //
376*8a978a17SVictor Perevertkin             // store the DriverGlobals pointer used to associate the driver
377*8a978a17SVictor Perevertkin             // with its client driver callback later in the callback hooks
378*8a978a17SVictor Perevertkin             //
379*8a978a17SVictor Perevertkin             context->CommonHeader.DriverGlobals = DriverGlobals;
380*8a978a17SVictor Perevertkin 
381*8a978a17SVictor Perevertkin             //
382*8a978a17SVictor Perevertkin             // add stored original callbacks to context
383*8a978a17SVictor Perevertkin             //
384*8a978a17SVictor Perevertkin             RtlCopyMemory(
385*8a978a17SVictor Perevertkin                 &context->IoQueueConfigOriginal,
386*8a978a17SVictor Perevertkin                 Config,
387*8a978a17SVictor Perevertkin                 sizeof(WDF_IO_QUEUE_CONFIG)
388*8a978a17SVictor Perevertkin                 );
389*8a978a17SVictor Perevertkin         }
390*8a978a17SVictor Perevertkin         else {
391*8a978a17SVictor Perevertkin             //
392*8a978a17SVictor Perevertkin             // For some reason adding context to handle failed. This should be
393*8a978a17SVictor Perevertkin             // rare failure, because context allocation was already successful,
394*8a978a17SVictor Perevertkin             // only adding to handle failed.
395*8a978a17SVictor Perevertkin             //
396*8a978a17SVictor Perevertkin             // Hooking failed if adding context is unsuccessful. This means
397*8a978a17SVictor Perevertkin             // kmdf has callbacks hooks but verifier cannot assiociate client
398*8a978a17SVictor Perevertkin             // driver callbacks with callback hooks. This would be a fatal error.
399*8a978a17SVictor Perevertkin             //
400*8a978a17SVictor Perevertkin             ASSERTMSG("KMDF Enhanced Verifier failed to add context to object "
401*8a978a17SVictor Perevertkin                 "handle\n", FALSE);
402*8a978a17SVictor Perevertkin 
403*8a978a17SVictor Perevertkin             if (contextHeader != NULL) {
404*8a978a17SVictor Perevertkin                 FxPoolFree(contextHeader);
405*8a978a17SVictor Perevertkin             }
406*8a978a17SVictor Perevertkin         }
407*8a978a17SVictor Perevertkin     }
408*8a978a17SVictor Perevertkin     else {
409*8a978a17SVictor Perevertkin         //
410*8a978a17SVictor Perevertkin         // DDI call to KMDF failed. Nothing to do by verifier. Just return
411*8a978a17SVictor Perevertkin         // kmdf's status after freeing context header memory.
412*8a978a17SVictor Perevertkin         //
413*8a978a17SVictor Perevertkin         if (contextHeader != NULL) {
414*8a978a17SVictor Perevertkin             FxPoolFree(contextHeader);
415*8a978a17SVictor Perevertkin         }
416*8a978a17SVictor Perevertkin     }
417*8a978a17SVictor Perevertkin 
418*8a978a17SVictor Perevertkin     return status;
419*8a978a17SVictor Perevertkin }
420*8a978a17SVictor Perevertkin 
421*8a978a17SVictor Perevertkin _Must_inspect_result_
422*8a978a17SVictor Perevertkin PVOID
423*8a978a17SVictor Perevertkin FASTCALL
VfWdfObjectGetTypedContext(__in WDFOBJECT Handle,__in PCWDF_OBJECT_CONTEXT_TYPE_INFO TypeInfo)424*8a978a17SVictor Perevertkin VfWdfObjectGetTypedContext(
425*8a978a17SVictor Perevertkin     __in
426*8a978a17SVictor Perevertkin     WDFOBJECT Handle,
427*8a978a17SVictor Perevertkin     __in
428*8a978a17SVictor Perevertkin     PCWDF_OBJECT_CONTEXT_TYPE_INFO TypeInfo
429*8a978a17SVictor Perevertkin     )
430*8a978a17SVictor Perevertkin /*++
431*8a978a17SVictor Perevertkin 
432*8a978a17SVictor Perevertkin Routine Description:
433*8a978a17SVictor Perevertkin     Retrieves the requested type from a handle
434*8a978a17SVictor Perevertkin 
435*8a978a17SVictor Perevertkin Arguments:
436*8a978a17SVictor Perevertkin     Handle - the handle to retrieve the context from
437*8a978a17SVictor Perevertkin     TypeInfo - global constant pointer which describes the type.  Since the pointer
438*8a978a17SVictor Perevertkin         value is unique in all of kernel space, we will perform a pointer compare
439*8a978a17SVictor Perevertkin         instead of a deep structure compare
440*8a978a17SVictor Perevertkin 
441*8a978a17SVictor Perevertkin Return Value:
442*8a978a17SVictor Perevertkin     A valid context pointere or NULL.  NULL is not a failure, querying for a type
443*8a978a17SVictor Perevertkin     not associated with the handle is a legitimate operation.
444*8a978a17SVictor Perevertkin 
445*8a978a17SVictor Perevertkin   --*/
446*8a978a17SVictor Perevertkin {
447*8a978a17SVictor Perevertkin     FxContextHeader* pHeader;
448*8a978a17SVictor Perevertkin     FxObject* pObject;
449*8a978a17SVictor Perevertkin     PFX_DRIVER_GLOBALS pFxDriverGlobals;
450*8a978a17SVictor Perevertkin     WDFOBJECT_OFFSET offset;
451*8a978a17SVictor Perevertkin 
452*8a978a17SVictor Perevertkin     PAGED_CODE_LOCKED();
453*8a978a17SVictor Perevertkin 
454*8a978a17SVictor Perevertkin     //
455*8a978a17SVictor Perevertkin     // Do not call FxObjectHandleGetPtr( , , FX_TYPE_OBJECT) because this is a
456*8a978a17SVictor Perevertkin     // hot spot / workhorse function that should be as efficient as possible.
457*8a978a17SVictor Perevertkin     //
458*8a978a17SVictor Perevertkin     // A call to FxObjectHandleGetPtr would :
459*8a978a17SVictor Perevertkin     // 1)  invoke a virtual call to QueryInterface
460*8a978a17SVictor Perevertkin     //
461*8a978a17SVictor Perevertkin     // 2)  ASSERT that the ref count of the object is > zero.  Since this is one
462*8a978a17SVictor Perevertkin     //     of the few functions that can be called in EvtObjectDestroy where the
463*8a978a17SVictor Perevertkin     //     ref count is zero, that is not a good side affect.
464*8a978a17SVictor Perevertkin     //
465*8a978a17SVictor Perevertkin     offset = 0;
466*8a978a17SVictor Perevertkin     pObject = FxObject::_GetObjectFromHandle(Handle, &offset);
467*8a978a17SVictor Perevertkin 
468*8a978a17SVictor Perevertkin     //
469*8a978a17SVictor Perevertkin     // Use the object's globals, not the caller's
470*8a978a17SVictor Perevertkin     //
471*8a978a17SVictor Perevertkin     pFxDriverGlobals = pObject->GetDriverGlobals();
472*8a978a17SVictor Perevertkin 
473*8a978a17SVictor Perevertkin     FxPointerNotNull(pFxDriverGlobals, Handle);
474*8a978a17SVictor Perevertkin     FxPointerNotNull(pFxDriverGlobals, TypeInfo);
475*8a978a17SVictor Perevertkin 
476*8a978a17SVictor Perevertkin     pHeader = pObject->GetContextHeader();
477*8a978a17SVictor Perevertkin 
478*8a978a17SVictor Perevertkin     for ( ; pHeader != NULL; pHeader = pHeader->NextHeader) {
479*8a978a17SVictor Perevertkin         if (pHeader->ContextTypeInfo == TypeInfo) {
480*8a978a17SVictor Perevertkin             return &pHeader->Context[0];
481*8a978a17SVictor Perevertkin         }
482*8a978a17SVictor Perevertkin     }
483*8a978a17SVictor Perevertkin 
484*8a978a17SVictor Perevertkin     PCHAR pGivenName;
485*8a978a17SVictor Perevertkin 
486*8a978a17SVictor Perevertkin     if (TypeInfo->ContextName != NULL) {
487*8a978a17SVictor Perevertkin         pGivenName = TypeInfo->ContextName;
488*8a978a17SVictor Perevertkin     }
489*8a978a17SVictor Perevertkin     else {
490*8a978a17SVictor Perevertkin         pGivenName = "<no typename given>";
491*8a978a17SVictor Perevertkin     }
492*8a978a17SVictor Perevertkin 
493*8a978a17SVictor Perevertkin     DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGDEVICE,
494*8a978a17SVictor Perevertkin                         "Attempting to get context type %s from WDFOBJECT 0x%p",
495*8a978a17SVictor Perevertkin                         pGivenName, Handle);
496*8a978a17SVictor Perevertkin 
497*8a978a17SVictor Perevertkin     return NULL;
498*8a978a17SVictor Perevertkin }
499*8a978a17SVictor Perevertkin 
500*8a978a17SVictor Perevertkin _Must_inspect_result_
501*8a978a17SVictor Perevertkin NTSTATUS
VfAllocateContext(__in PWDF_DRIVER_GLOBALS DriverGlobals,__in PWDF_OBJECT_ATTRIBUTES Attributes,__out PVOID * ContextHeader)502*8a978a17SVictor Perevertkin VfAllocateContext(
503*8a978a17SVictor Perevertkin     __in PWDF_DRIVER_GLOBALS DriverGlobals,
504*8a978a17SVictor Perevertkin     __in PWDF_OBJECT_ATTRIBUTES Attributes,
505*8a978a17SVictor Perevertkin     __out PVOID* ContextHeader
506*8a978a17SVictor Perevertkin     )
507*8a978a17SVictor Perevertkin /*++
508*8a978a17SVictor Perevertkin 
509*8a978a17SVictor Perevertkin Routine Description:
510*8a978a17SVictor Perevertkin     Allocates an additional context on the handle if the object is in the
511*8a978a17SVictor Perevertkin     correct state
512*8a978a17SVictor Perevertkin 
513*8a978a17SVictor Perevertkin Arguments:
514*8a978a17SVictor Perevertkin     Handle - handle on which to add a context
515*8a978a17SVictor Perevertkin     Attributes - attributes which describe the type and size of the new context
516*8a978a17SVictor Perevertkin     Context - optional pointer which will recieve the new context
517*8a978a17SVictor Perevertkin 
518*8a978a17SVictor Perevertkin Return Value:
519*8a978a17SVictor Perevertkin     STATUS_SUCCESS upon success, STATUS_OBJECT_NAME_EXISTS if the context type
520*8a978a17SVictor Perevertkin     is already attached to the handle, and !NT_SUCCESS on failure
521*8a978a17SVictor Perevertkin 
522*8a978a17SVictor Perevertkin   --*/
523*8a978a17SVictor Perevertkin {
524*8a978a17SVictor Perevertkin     PFX_DRIVER_GLOBALS pFxDriverGlobals;
525*8a978a17SVictor Perevertkin     NTSTATUS status;
526*8a978a17SVictor Perevertkin     FxContextHeader *pHeader;
527*8a978a17SVictor Perevertkin     size_t size;
528*8a978a17SVictor Perevertkin 
529*8a978a17SVictor Perevertkin     PAGED_CODE_LOCKED();
530*8a978a17SVictor Perevertkin 
531*8a978a17SVictor Perevertkin     pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals);
532*8a978a17SVictor Perevertkin 
533*8a978a17SVictor Perevertkin     status = FxValidateObjectAttributes(
534*8a978a17SVictor Perevertkin         pFxDriverGlobals, Attributes, FX_VALIDATE_OPTION_ATTRIBUTES_REQUIRED);
535*8a978a17SVictor Perevertkin     if (!NT_SUCCESS(status)) {
536*8a978a17SVictor Perevertkin         return status;
537*8a978a17SVictor Perevertkin     }
538*8a978a17SVictor Perevertkin 
539*8a978a17SVictor Perevertkin     //
540*8a978a17SVictor Perevertkin     // Must have a context type!
541*8a978a17SVictor Perevertkin     //
542*8a978a17SVictor Perevertkin     if (Attributes->ContextTypeInfo == NULL) {
543*8a978a17SVictor Perevertkin         status = STATUS_OBJECT_NAME_INVALID;
544*8a978a17SVictor Perevertkin         DoTraceLevelMessage(
545*8a978a17SVictor Perevertkin             pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGHANDLE,
546*8a978a17SVictor Perevertkin             "Attributes %p ContextTypeInfo is NULL, %!STATUS!",
547*8a978a17SVictor Perevertkin             Attributes, status);
548*8a978a17SVictor Perevertkin         return status;
549*8a978a17SVictor Perevertkin     }
550*8a978a17SVictor Perevertkin 
551*8a978a17SVictor Perevertkin     //
552*8a978a17SVictor Perevertkin     // By passing 0's for raw object size and extra size, we can compute the
553*8a978a17SVictor Perevertkin     // size of only the header and its contents.
554*8a978a17SVictor Perevertkin     //
555*8a978a17SVictor Perevertkin     status = FxCalculateObjectTotalSize(pFxDriverGlobals, 0, 0, Attributes, &size);
556*8a978a17SVictor Perevertkin     if (!NT_SUCCESS(status)) {
557*8a978a17SVictor Perevertkin         return status;
558*8a978a17SVictor Perevertkin     }
559*8a978a17SVictor Perevertkin 
560*8a978a17SVictor Perevertkin     pHeader = (FxContextHeader*)
561*8a978a17SVictor Perevertkin          FxPoolAllocate(pFxDriverGlobals, NonPagedPool, size);
562*8a978a17SVictor Perevertkin 
563*8a978a17SVictor Perevertkin     if (pHeader != NULL) {
564*8a978a17SVictor Perevertkin         *ContextHeader = pHeader;
565*8a978a17SVictor Perevertkin         status = STATUS_SUCCESS;
566*8a978a17SVictor Perevertkin     }
567*8a978a17SVictor Perevertkin     else {
568*8a978a17SVictor Perevertkin         status = STATUS_INSUFFICIENT_RESOURCES;
569*8a978a17SVictor Perevertkin     }
570*8a978a17SVictor Perevertkin 
571*8a978a17SVictor Perevertkin     return status;
572*8a978a17SVictor Perevertkin }
573*8a978a17SVictor Perevertkin 
574*8a978a17SVictor Perevertkin _Must_inspect_result_
575*8a978a17SVictor Perevertkin NTSTATUS
VfAddContextToHandle(__in PVOID ContextHeader,__in PWDF_OBJECT_ATTRIBUTES Attributes,__in WDFOBJECT Handle,__out_opt PVOID * Context)576*8a978a17SVictor Perevertkin VfAddContextToHandle(
577*8a978a17SVictor Perevertkin     __in PVOID ContextHeader,
578*8a978a17SVictor Perevertkin     __in PWDF_OBJECT_ATTRIBUTES Attributes,
579*8a978a17SVictor Perevertkin     __in WDFOBJECT Handle,
580*8a978a17SVictor Perevertkin     __out_opt PVOID* Context
581*8a978a17SVictor Perevertkin     )
582*8a978a17SVictor Perevertkin {
583*8a978a17SVictor Perevertkin     PFX_DRIVER_GLOBALS pFxDriverGlobals;
584*8a978a17SVictor Perevertkin     NTSTATUS status = STATUS_SUCCESS;
585*8a978a17SVictor Perevertkin     FxObject* pObject;
586*8a978a17SVictor Perevertkin     FxContextHeader *pHeader;
587*8a978a17SVictor Perevertkin     WDFOBJECT_OFFSET offset;
588*8a978a17SVictor Perevertkin 
589*8a978a17SVictor Perevertkin     PAGED_CODE_LOCKED();
590*8a978a17SVictor Perevertkin 
591*8a978a17SVictor Perevertkin     pHeader = (FxContextHeader *)ContextHeader;
592*8a978a17SVictor Perevertkin 
593*8a978a17SVictor Perevertkin     if (pHeader == NULL) {
594*8a978a17SVictor Perevertkin         return STATUS_INVALID_PARAMETER;
595*8a978a17SVictor Perevertkin     }
596*8a978a17SVictor Perevertkin 
597*8a978a17SVictor Perevertkin     //
598*8a978a17SVictor Perevertkin     // No need to call FxObjectHandleGetPtr( , , FX_TYPE_OBJECT) because
599*8a978a17SVictor Perevertkin     // we assume that the object handle will point back to an FxObject.  (The
600*8a978a17SVictor Perevertkin     // call to FxObjectHandleGetPtr will just make needless virtual call into
601*8a978a17SVictor Perevertkin     // FxObject anyways).
602*8a978a17SVictor Perevertkin     //
603*8a978a17SVictor Perevertkin     offset = 0;
604*8a978a17SVictor Perevertkin     pObject = FxObject::_GetObjectFromHandle(Handle, &offset);
605*8a978a17SVictor Perevertkin 
606*8a978a17SVictor Perevertkin     pFxDriverGlobals = pObject->GetDriverGlobals();
607*8a978a17SVictor Perevertkin 
608*8a978a17SVictor Perevertkin     if (offset != 0) {
609*8a978a17SVictor Perevertkin         //
610*8a978a17SVictor Perevertkin         // for lack of a better error code
611*8a978a17SVictor Perevertkin         //
612*8a978a17SVictor Perevertkin         status = STATUS_OBJECT_PATH_INVALID;
613*8a978a17SVictor Perevertkin 
614*8a978a17SVictor Perevertkin         DoTraceLevelMessage(
615*8a978a17SVictor Perevertkin             pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGHANDLE,
616*8a978a17SVictor Perevertkin             "WDFHANDLE %p cannot have contexts added to it, %!STATUS!",
617*8a978a17SVictor Perevertkin             Handle, status);
618*8a978a17SVictor Perevertkin 
619*8a978a17SVictor Perevertkin         goto cleanup;
620*8a978a17SVictor Perevertkin     }
621*8a978a17SVictor Perevertkin 
622*8a978a17SVictor Perevertkin     pObject->ADDREF(&status);
623*8a978a17SVictor Perevertkin 
624*8a978a17SVictor Perevertkin     FxContextHeaderInit(pHeader, pObject, Attributes);
625*8a978a17SVictor Perevertkin 
626*8a978a17SVictor Perevertkin     status = pObject->AddContext(pHeader, Context, Attributes);
627*8a978a17SVictor Perevertkin 
628*8a978a17SVictor Perevertkin     //
629*8a978a17SVictor Perevertkin     // STATUS_OBJECT_NAME_EXISTS will not fail NT_SUCCESS, so check
630*8a978a17SVictor Perevertkin     // explicitly for STATUS_SUCCESS.
631*8a978a17SVictor Perevertkin     //
632*8a978a17SVictor Perevertkin     if (status != STATUS_SUCCESS) {
633*8a978a17SVictor Perevertkin         DoTraceLevelMessage(
634*8a978a17SVictor Perevertkin             pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGHANDLE,
635*8a978a17SVictor Perevertkin             "WDFHANDLE %p failed to add context, %!STATUS!",
636*8a978a17SVictor Perevertkin             Handle, status);
637*8a978a17SVictor Perevertkin     }
638*8a978a17SVictor Perevertkin 
639*8a978a17SVictor Perevertkin     pObject->RELEASE(&status);
640*8a978a17SVictor Perevertkin 
641*8a978a17SVictor Perevertkin cleanup:
642*8a978a17SVictor Perevertkin 
643*8a978a17SVictor Perevertkin     if (status != STATUS_SUCCESS && pHeader != NULL) {
644*8a978a17SVictor Perevertkin         FxPoolFree(pHeader);
645*8a978a17SVictor Perevertkin     }
646*8a978a17SVictor Perevertkin 
647*8a978a17SVictor Perevertkin     return status;
648*8a978a17SVictor Perevertkin }
649*8a978a17SVictor Perevertkin 
650*8a978a17SVictor Perevertkin } // extern "C"
651