1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxIoTargetRemoteKm.cpp
8 
9 Abstract:
10 
11 Author:
12 
13 Environment:
14 
15     kernel mode only
16 
17 Revision History:
18 
19 --*/
20 
21 #include "../../fxtargetsshared.hpp"
22 
23 extern "C" {
24 // #include "FxIoTargetRemoteKm.tmh"
25 }
26 
27 #include <initguid.h>
28 #include "wdmguid.h"
29 
30 _Must_inspect_result_
31 NTSTATUS
32 FxIoTargetRemote::_PlugPlayNotification(
33     __in PVOID NotificationStructure,
34     __inout_opt PVOID Context
35     )
36 {
37     PFX_DRIVER_GLOBALS pFxDriverGlobals;
38     PTARGET_DEVICE_REMOVAL_NOTIFICATION pNotification;
39     FxIoTargetRemote* pThis;
40     NTSTATUS status;
41 
42     ASSERT(Mx::MxGetCurrentIrql() < DISPATCH_LEVEL);
43     pNotification = (PTARGET_DEVICE_REMOVAL_NOTIFICATION) NotificationStructure;
44     pThis = (FxIoTargetRemote*) Context;
45 
46     //
47     // In one of these callbacks, the driver may decide to delete the target.
48     // If that is the case, we need to be able to return and deref the object until
49     // we are done.
50     //
51     pThis->ADDREF((PVOID)_PlugPlayNotification);
52 
53     pFxDriverGlobals = pThis->GetDriverGlobals();
54 
55     status = STATUS_SUCCESS;
56 
57     if (FxIsEqualGuid(&pNotification->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE)) {
58 
59         DoTraceLevelMessage(
60             pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
61             "WDFIOTARGET %p: query remove notification", pThis->GetObjectHandle());
62 
63         //
64         // Device is gracefully being removed.  PnP is asking us to close down
65         // the target.  If there is a driver callback, there is *no* default
66         // behavior.  This is because we don't know what the callback is going
67         // to do.  For instance, the driver could reopen the target to a
68         // different device in a multi-path scenario.
69         //
70         if (pThis->m_EvtQueryRemove.m_Method != NULL) {
71             status = pThis->m_EvtQueryRemove.Invoke(pThis->GetHandle());
72         }
73         else {
74             DoTraceLevelMessage(
75                 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
76                 "WDFIOTARGET %p: query remove, default action (close for QR)",
77                 pThis->GetObjectHandle());
78 
79             //
80             // No callback, close it down conditionally.
81             //
82             pThis->Close(FxIoTargetRemoteCloseReasonQueryRemove);
83         }
84     }
85     else if (FxIsEqualGuid(&pNotification->Event, &GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
86 
87         DoTraceLevelMessage(
88             pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
89             "WDFIOTARGET %p: remove complete notification", pThis->GetObjectHandle());
90 
91         //
92         // The device was surprise removed, close it for good if the driver has
93         // no override.
94         //
95         if (pThis->m_EvtRemoveComplete.m_Method != NULL) {
96             pThis->m_EvtRemoveComplete.Invoke(pThis->GetHandle());
97         }
98         else {
99             DoTraceLevelMessage(
100                 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
101                 "WDFIOTARGET %p: remove complete, default action (close)",
102                 pThis->GetObjectHandle());
103 
104             //
105             // The device is now gone for good.  Close down the target for good.
106             //
107             pThis->Close(FxIoTargetRemoteCloseReasonPlainClose);
108         }
109     }
110     else if (FxIsEqualGuid(&pNotification->Event, &GUID_TARGET_DEVICE_REMOVE_CANCELLED)) {
111 
112         DoTraceLevelMessage(
113             pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
114             "WDFIOTARGET %p: remove canceled notification", pThis->GetObjectHandle());
115 
116         if (pThis->m_EvtRemoveCanceled.m_Method != NULL) {
117             pThis->m_EvtRemoveCanceled.Invoke(pThis->GetHandle());
118         }
119         else {
120             WDF_IO_TARGET_OPEN_PARAMS params;
121 
122             DoTraceLevelMessage(
123                 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
124                 "WDFIOTARGET %p: remove canceled, default action (reopen)",
125                 pThis->GetObjectHandle());
126 
127             WDF_IO_TARGET_OPEN_PARAMS_INIT_REOPEN(&params);
128 
129             //
130             // Attempt to reopen the target with stored settings
131             //
132             status = pThis->Open(&params);
133         }
134     }
135 
136     pThis->RELEASE((PVOID)_PlugPlayNotification);
137 
138     return status;
139 }
140 
141 NTSTATUS
142 FxIoTargetRemote::RegisterForPnpNotification(
143     )
144 {
145     NTSTATUS status;
146 
147     //
148     // Register for PNP notifications on the handle we just opened.
149     // This will notify us of pnp state changes on the handle.
150     //
151     status = IoRegisterPlugPlayNotification(
152         EventCategoryTargetDeviceChange,
153         0,
154         m_TargetFileObject,
155         m_Driver->GetDriverObject(),
156         _PlugPlayNotification,
157         this,
158         &m_TargetNotifyHandle);
159 
160     return status;
161 }
162 
163 VOID
164 FxIoTargetRemote::UnregisterForPnpNotification(
165     _In_ MdTargetNotifyHandle Handle
166     )
167 {
168     if (Handle != NULL) {
169 
170 
171 
172 
173 
174 
175 
176 
177 
178 
179 
180 
181 
182 
183 
184 
185 
186 
187 
188 
189 
190 
191 
192         if (FxLibraryGlobals.IoUnregisterPlugPlayNotificationEx != NULL) {
193             FxLibraryGlobals.IoUnregisterPlugPlayNotificationEx(Handle);
194         }
195         else {
196             IoUnregisterPlugPlayNotification(Handle);
197         }
198     }
199 }
200 
201 NTSTATUS
202 FxIoTargetRemote::OpenTargetHandle(
203     _In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams,
204     _Inout_ FxIoTargetRemoveOpenParams* pParams
205     )
206 {
207     OBJECT_ATTRIBUTES oa;
208     IO_STATUS_BLOCK ioStatus;
209     NTSTATUS status;
210 
211     InitializeObjectAttributes(&oa,
212                                &pParams->TargetDeviceName,
213                                OBJ_KERNEL_HANDLE,
214                                NULL,
215                                NULL);
216 
217     status = ZwCreateFile(&m_TargetHandle,
218                           pParams->DesiredAccess,
219                           &oa,
220                           &ioStatus,
221                           pParams->AllocationSizePointer,
222                           pParams->FileAttributes,
223                           pParams->ShareAccess,
224                           pParams->CreateDisposition,
225                           pParams->CreateOptions,
226                           pParams->EaBuffer,
227                           pParams->EaBufferLength);
228 
229     OpenParams->FileInformation = (ULONG)ioStatus.Information;
230 
231     if (NT_SUCCESS(status)) {
232         DoTraceLevelMessage(
233             GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGIOTARGET,
234             "ZwCreateFile for WDFIOTARGET %p returned status %!STATUS!, info 0x%x",
235             GetObjectHandle(), status, (ULONG) ioStatus.Information);
236 
237         //
238         // The open operation was successful. Dereference the file handle and
239         // obtain a pointer to the device object for the handle.
240         //
241         status = ObReferenceObjectByHandle(
242             m_TargetHandle,
243             pParams->DesiredAccess,
244             *IoFileObjectType,
245             KernelMode,
246             (PVOID*) &m_TargetFileObject,
247             NULL);
248 
249         if (NT_SUCCESS(status)) {
250             m_TargetDevice = IoGetRelatedDeviceObject(m_TargetFileObject);
251 
252             if (m_TargetDevice == NULL) {
253                 DoTraceLevelMessage(
254                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
255                     "WDFIOTARGET %p, could not convert filobj %p to devobj",
256                     GetObjectHandle(), m_TargetFileObject);
257 
258                 status = STATUS_NO_SUCH_DEVICE;
259             }
260         }
261         else {
262             DoTraceLevelMessage(
263                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
264                 "WDFIOTARGET %p, could not convert handle %p to fileobject, "
265                 "status %!STATUS!",
266                 GetObjectHandle(), m_TargetHandle, status);
267         }
268     }
269     else {
270         DoTraceLevelMessage(
271             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
272             "ZwCreateFile for WDFIOTARGET %p returned status %!STATUS!, info 0x%x",
273             GetObjectHandle(), status, (ULONG) ioStatus.Information);
274     }
275 
276     return status;
277 }
278 
279 NTSTATUS
280 FxIoTargetRemote::GetTargetDeviceRelations(
281     _Inout_ BOOLEAN* Close
282     )
283 {
284     PDEVICE_OBJECT pTopOfStack;
285     FxAutoIrp irp(NULL);
286     PIRP pIrp;
287     NTSTATUS status;
288 
289     pTopOfStack = IoGetAttachedDeviceReference(m_TargetDevice);
290 
291     pIrp = IoAllocateIrp(pTopOfStack->StackSize, FALSE);
292 
293     if (pIrp != NULL) {
294         PIO_STACK_LOCATION stack;
295 
296         irp.SetIrp(pIrp);
297 
298         stack = irp.GetNextIrpStackLocation();
299         stack->MajorFunction = IRP_MJ_PNP;
300         stack->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
301         stack->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
302 
303         //
304         // Initialize the status to error in case the bus driver decides not
305         // to set it correctly.
306         //
307         irp.SetStatus(STATUS_NOT_SUPPORTED);
308 
309         status = irp.SendIrpSynchronously(pTopOfStack);
310 
311         if (NT_SUCCESS(status)) {
312             PDEVICE_RELATIONS pRelations;
313 
314             pRelations = (PDEVICE_RELATIONS) irp.GetInformation();
315 
316             ASSERT(pRelations != NULL);
317 
318             //
319             // m_TargetPdo was referenced by the bus driver, it will be
320             // dereferenced when the target is closed.
321             //
322             m_TargetPdo = pRelations->Objects[0];
323 
324             //
325             // We, as the caller, are responsible for freeing the relations
326             // that the bus driver allocated.
327             //
328             ExFreePool(pRelations);
329         }
330         else {
331             //
332             // Could not retrieve the PDO pointer, error handled later
333             //
334             DO_NOTHING();
335         }
336     }
337     else {
338         //
339         // Could not even allocate an irp, failure.
340         //
341         status = STATUS_INSUFFICIENT_RESOURCES;
342         DoTraceLevelMessage(
343         GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
344         "Unable to allocate memory for IRP WDFIOTARGET %p, %!STATUS!",
345         GetObjectHandle(), status);
346     }
347 
348     //
349     // Only fail the open if we cannot allocate an irp or if the lower
350     // driver could not allocate a relations.
351     //
352     if (status == STATUS_INSUFFICIENT_RESOURCES) {
353         *Close = TRUE;
354     }
355     else {
356         status = STATUS_SUCCESS;
357     }
358 
359     //
360     // Remove the reference taken by IoGetAttachedDeviceReference
361     //
362     ObDereferenceObject(pTopOfStack);
363 
364     return status;
365 }
366 
367