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