1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxIoTargetRemoteUm.cpp
8 
9 Abstract:
10 
11 Author:
12 
13 Environment:
14 
15     user mode only
16 
17 Revision History:
18 
19 --*/
20 
21 #include "..\..\FxTargetsShared.hpp"
22 
23 extern "C" {
24 #include "FxIoTargetRemoteUm.tmh"
25 }
26 
27 #include <initguid.h>
28 #include "wdmguid.h"
29 
30 NTSTATUS
InitRemoteModeSpecific(__in FxDeviceBase * Device)31 FxIoTargetRemote::InitRemoteModeSpecific(
32     __in FxDeviceBase* Device
33     )
34 {
35     NTSTATUS status;
36     HRESULT hr;
37     IWudfDeviceStack* devStack;
38 
39     devStack = Device->GetDeviceObject()->GetDeviceStackInterface();
40 
41     //
42     // Event initialization can fail in UM so initialize it now instead of in
43     // constructor.
44     //
45     status = m_OpenedEvent.Initialize();
46     if (!NT_SUCCESS(status)) {
47         DoTraceLevelMessage(
48             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
49             "Failed to initialize m_OpenedEvent, %!STATUS!", status);
50         return status;
51     }
52 
53     //
54     // Create remote dispatcher.
55     // This calls directly into the Host to create.
56     // For most IoTargets the dispatcher is hidden from the Fx, but
57     // for the RemoteTarget, we need to directly dispatch I/O to
58     // a win32 handle, regardless of what dispatch method the device
59     // is set to use in it's INF.
60     //
61     hr = devStack->CreateRemoteDispatcher(&m_pIoDispatcher,
62                                           &m_pRemoteDispatcher);
63 
64     if (FAILED(hr)) {
65         status = FxDevice::NtStatusFromHr(devStack, hr);
66         DoTraceLevelMessage(
67             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
68             "Failed to Create RemoteDispatcher, %!STATUS!", status);
69         return status;
70     }
71 
72     return status;
73 }
74 
75 VOID
RemoveModeSpecific(VOID)76 FxIoTargetRemote::RemoveModeSpecific(
77     VOID
78     )
79 
80 {
81     //
82     // Delete callback object
83     //
84     if (m_NotificationCallback != NULL) {
85         delete m_NotificationCallback;
86         m_NotificationCallback = NULL;
87     }
88 
89     SAFE_RELEASE(m_pIoDispatcher);
90     SAFE_RELEASE(m_pRemoteDispatcher);
91 }
92 
93 NTSTATUS
OpenTargetHandle(_In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams,_Inout_ FxIoTargetRemoveOpenParams * pParams)94 FxIoTargetRemote::OpenTargetHandle(
95     _In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams,
96     _Inout_ FxIoTargetRemoveOpenParams* pParams
97     )
98 {
99     NTSTATUS status;
100     HRESULT hr = S_OK;
101     HANDLE hTarget;
102     ULONG flagsAndAttributes;
103 
104     FX_VERIFY_WITH_NAME(INTERNAL,
105               VERIFY(INVALID_HANDLE_VALUE == m_pRemoteDispatcher->GetHandle()),
106               GetDriverGlobals()->Public.DriverName);
107 
108     //
109     // UMDF 1.11 allowed following fields to be set by caller.
110     //  DWORD dwDesiredAccess
111     //  typedef struct _UMDF_IO_TARGET_OPEN_PARAMS
112     //  {
113     //      DWORD dwShareMode;   //
114     //      DWORD dwCreationDisposition;
115     //      DWORD dwFlagsAndAttributes;
116     //  } UMDF_IO_TARGET_OPEN_PARAMS;
117     //
118     //
119     // We always use overlapped I/O
120     //
121     flagsAndAttributes = pParams->FileAttributes | FILE_FLAG_OVERLAPPED;
122 
123     hTarget = CreateFile(pParams->TargetDeviceName.Buffer,
124                          pParams->DesiredAccess,         // dwDesiredAccess
125                          pParams->ShareAccess,           // dwShareMode
126                          NULL,                           // lpSecurityAttributes
127                          pParams->CreateDisposition,     // dwCreationDisposition
128                          flagsAndAttributes,             // dwFlagsAndAttributes
129                          NULL);
130 
131     if (INVALID_HANDLE_VALUE == hTarget) {
132         hr = HRESULT_FROM_WIN32(GetLastError());
133         status = m_Device->NtStatusFromHr(hr);
134 
135         DoTraceLevelMessage(
136             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
137             "CreateFile for WDFIOTARGET %p returned status %!STATUS!",
138             GetObjectHandle(), status);
139 
140         FX_VERIFY_WITH_NAME(INTERNAL, VERIFY(FAILED(hr)), GetDriverGlobals()->Public.DriverName);
141     }
142     else {
143         m_TargetHandle = hTarget;
144         status = STATUS_SUCCESS;
145     }
146 
147     return status;
148 }
149 
150 HANDLE
GetTargetHandle(VOID)151 FxIoTargetRemote::GetTargetHandle(
152     VOID
153     )
154 {
155     HRESULT hrQi;
156     IWudfFile2* pFile;
157     HANDLE handle = m_TargetHandle;
158 
159     if (m_OpenParams.OpenType == WdfIoTargetOpenLocalTargetByFile) {
160         if (m_TargetFileObject == NULL) {
161             DoTraceLevelMessage(
162                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
163                 "WDFIOTARGET %p has no target file object, could not get handle",
164                 GetObjectHandle());
165         }
166         else {
167             ASSERT(m_TargetHandle == NULL);
168 
169             hrQi = m_TargetFileObject->QueryInterface(IID_IWudfFile2, (PVOID*)&pFile);
170             FX_VERIFY(INTERNAL, CHECK_QI(hrQi, pFile));
171             pFile->Release();
172 
173             handle = pFile->GetWeakRefHandle();
174         }
175     }
176 
177     //
178     // Normalize the invalid handle value returned by CreateFile in host
179     // to what's expected by the WdfIoTargetWdmGetTargetFileHandle caller.
180     //
181     if (handle == INVALID_HANDLE_VALUE) {
182         handle = NULL;
183     }
184 
185     return handle;
186 }
187 
188 NTSTATUS
BindToHandle(VOID)189 FxIoTargetRemote::BindToHandle(
190     VOID
191     )
192 {
193     NTSTATUS status;
194     HRESULT hr;
195 
196     //
197     // Tell the RemoteDispatcher to bind to the new handle.
198     //
199     hr = m_pRemoteDispatcher->BindToHandle(m_TargetHandle);
200     if (FAILED(hr)) {
201         status = m_Device->NtStatusFromHr(hr);
202         DoTraceLevelMessage(
203             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
204             "WDFIOTARGET %p could not bind remote dispatcher to new handle, "
205             "%!STATUS!", GetObjectHandle(), status);
206         return status;
207     }
208 
209     status = STATUS_SUCCESS;
210     return status;
211 }
212 
213 void
UnbindHandle(_In_ FxIoTargetClearedPointers * TargetPointers)214 FxIoTargetRemote::UnbindHandle(
215     _In_ FxIoTargetClearedPointers* TargetPointers
216     )
217 {
218     if (NULL != m_pRemoteDispatcher) {
219         //
220         // Close the handle we gave to the RemoteDispatcher
221         //
222         // NOTE: IWudfRemoteDispatcher::CloseHandle can be safely called even if
223         // we've not previously given it a handle. In this case, it does
224         // nothing.
225         //
226         DoTraceLevelMessage(
227             GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
228             "WDFIOTARGET %p Unbinding RemoteDispatcher %p to handle %p on close",
229             GetObjectHandle(), m_pRemoteDispatcher, TargetPointers->TargetHandle);
230 
231         m_pRemoteDispatcher->CloseHandle();
232 
233         //
234         // Host closes the handle in CloseHandle call above so set the handle
235         // in TargetPointers to NULL.
236         //
237         TargetPointers->TargetHandle = NULL;
238     }
239 }
240 
241 NTSTATUS
GetTargetDeviceRelations(_Inout_ BOOLEAN * Close)242 FxIoTargetRemote::GetTargetDeviceRelations(
243     _Inout_ BOOLEAN* Close
244     )
245 {
246     UNREFERENCED_PARAMETER(Close);
247 
248     //
249     // Not needed for UMDF
250     //
251     DO_NOTHING();
252 
253     return STATUS_SUCCESS;
254 }
255 
256 NTSTATUS
RegisterForPnpNotification(VOID)257 FxIoTargetRemote::RegisterForPnpNotification(
258     VOID
259     )
260 {
261     NTSTATUS status = STATUS_SUCCESS;
262     HRESULT hr;
263     FxIoTargetRemoteNotificationCallback* callback;
264 
265     UNREFERENCED_PARAMETER(hr);
266 
267     //
268     // Allocate callback object
269     //
270     if (m_NotificationCallback == NULL) {
271         callback = new (GetDriverGlobals())
272             FxIoTargetRemoteNotificationCallback(GetDriverGlobals(), this);
273 
274         if (callback == NULL) {
275             status = STATUS_INSUFFICIENT_RESOURCES;
276             DoTraceLevelMessage(
277                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
278                 "WDFIOTARGET %p could not allocate resources for "
279                 "notification registration, %!STATUS!",
280                 GetObjectHandle(), status);
281             return status;
282         }
283 
284         m_NotificationCallback = callback;
285     }
286 
287     //
288     // Register for Target Device Change notifications
289     // These notifications will arrive asynchronously.
290     //
291     IWudfDeviceStack * pDevStack = m_Device->GetDeviceStack();
292 
293     hr = pDevStack->RegisterTargetDeviceNotification(
294              static_cast<IWudfTargetCallbackDeviceChange *> (m_NotificationCallback),
295              m_TargetHandle,
296              &m_TargetNotifyHandle);
297 
298     if (FAILED(hr)) {
299         if (m_NotificationCallback != NULL) {
300             delete m_NotificationCallback;
301             m_NotificationCallback = NULL;
302         }
303 
304         status = m_Device->NtStatusFromHr(hr);
305         DoTraceLevelMessage(
306             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
307             "WDFIOTARGET %p failed to register for Pnp notification, %!STATUS!",
308             GetObjectHandle(), status);
309         return status;
310     }
311 
312     status = STATUS_SUCCESS;
313     DoTraceLevelMessage(
314         GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
315         "WDFIOTARGET %p registered for Pnp notification, %!STATUS!",
316         GetObjectHandle(), status);
317 
318     return status;
319 }
320 
321 VOID
UnregisterForPnpNotification(_In_ MdTargetNotifyHandle NotifyHandle)322 FxIoTargetRemote::UnregisterForPnpNotification(
323     _In_ MdTargetNotifyHandle NotifyHandle
324     )
325 {
326     //
327     // check if we previously registered
328     //
329     if (NotifyHandle == WUDF_TARGET_CONTEXT_INVALID) {
330         return;
331     }
332 
333     //
334     // Unregister.
335     //
336     IWudfDeviceStack * pDevStack = m_Device->GetDeviceStack();
337     pDevStack->UnregisterTargetDeviceNotification(NotifyHandle);
338 
339 }
340 
341 NTSTATUS
OpenLocalTargetByFile(_In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams)342 FxIoTargetRemote::OpenLocalTargetByFile(
343     _In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams
344     )
345 {
346     NTSTATUS status;
347 
348     //
349     // OpenParams must be non NULL b/c we can't reopen a target with a
350     // previous device object.
351     //
352     ASSERT(OpenParams->Type == WdfIoTargetOpenLocalTargetByFile);
353     m_OpenParams.OpenType = OpenParams->Type;
354 
355     //
356     // Create a file object. This is UM-specific feature, where host opens
357     // the reflector control device (optionally supplying the reference string
358     // provided by caller). If there are lower device drivers in the um stack,
359     // host sends them IRP_MJ_CREATE as well. The lower drivers in kernel see
360     // IRP_MJ_CREATE as well as a result of opening the reflector control
361     // object.
362     // Note that m_TargetDevice is already set to next lower device during init.
363     //
364     status = CreateWdfFileObject(&OpenParams->FileName,
365                                  &m_TargetFileObject);
366 
367     if (!NT_SUCCESS(status)) {
368         DoTraceLevelMessage(
369             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
370             "Failed to create WDF File Object, %!STATUS!", status);
371         return status;
372     }
373 
374     //
375     // Target handle is not used in this type of open.
376     //
377     m_TargetHandle = NULL;
378 
379     //
380     // By taking a manual reference here, we simplify the code in
381     // FxIoTargetRemote::Close where we can assume there is an outstanding
382     // reference on the WDM file object at all times as long as we have a non
383     // NULL pointer.
384     //
385     if (m_TargetFileObject != NULL) {
386         Mx::MxReferenceObject(m_TargetFileObject);
387     }
388 
389     return status;
390 }
391 
392 NTSTATUS
CreateWdfFileObject(_In_opt_ PUNICODE_STRING FileName,_Out_ MdFileObject * FileObject)393 FxIoTargetRemote::CreateWdfFileObject(
394     _In_opt_ PUNICODE_STRING  FileName,
395     _Out_ MdFileObject* FileObject
396     )
397 {
398     HRESULT hr = S_OK;
399     NTSTATUS status;
400     MdFileObject wdmFileObject = NULL;
401 
402     FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), CHECK_NOT_NULL(FileObject),
403         GetDriverGlobals()->Public.DriverName);
404 
405     *FileObject = NULL;
406 
407     hr = m_Device->GetDeviceStack()->CreateWdfFile(
408                             m_Device->GetDeviceObject(),
409                             m_Device->GetAttachedDevice(),
410                             FileName->Buffer,
411                             &wdmFileObject
412                             );
413     if (SUCCEEDED(hr)) {
414         *FileObject = wdmFileObject;
415         status = STATUS_SUCCESS;
416     }
417     else {
418         status = m_Device->NtStatusFromHr(hr);
419     }
420 
421     return status;
422 }
423 
424 VOID
CloseWdfFileObject(_In_ MdFileObject FileObject)425 FxIoTargetRemote::CloseWdfFileObject(
426     _In_ MdFileObject FileObject
427     )
428 {
429    m_Device->GetDeviceStack()->CloseFile(FileObject);
430    SAFE_RELEASE(FileObject);
431 }
432 
433 BOOL
434 __stdcall
OnQueryRemove(_In_ WUDF_TARGET_CONTEXT RegistrationID)435 FxIoTargetRemoteNotificationCallback::OnQueryRemove(
436     _In_ WUDF_TARGET_CONTEXT RegistrationID
437     )
438 {
439     PFX_DRIVER_GLOBALS pFxDriverGlobals;
440     FxIoTargetRemote* pThis;
441     NTSTATUS status;
442     BOOLEAN bStatus;
443 
444     pThis = m_RemoteTarget;
445 
446     //
447     // In one of these callbacks, the driver may decide to delete the target.
448     // If that is the case, we need to be able to return and deref the object until
449     // we are done.
450     //
451     pThis->ADDREF(m_RemoteTarget);
452 
453     pFxDriverGlobals = pThis->GetDriverGlobals();
454 
455     status = STATUS_SUCCESS;
456     bStatus = TRUE;
457 
458     if (GetRegistrationId() != RegistrationID) {
459         //
460         // By design, we can get notification callbacks even after we have
461         // unregistered for notifications. This can happen if there were
462         // callbacks already in flight before we unregistered. In this case, we
463         // simply succeed on query-remove. Since we have already unregistered,
464         // there is no reason for us to fail query-remove.
465         //
466         DoTraceLevelMessage(
467             pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGIOTARGET,
468                     "QueryRemove callback was for an old registration, ignoring.");
469 
470         bStatus = TRUE;
471         goto exit;
472     }
473 
474     DoTraceLevelMessage(
475         pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
476         "WDFIOTARGET %p: query remove notification",
477         m_RemoteTarget->GetObjectHandle());
478 
479     //
480     // Device is gracefully being removed.  PnP is asking us to close down
481     // the target.  If there is a driver callback, there is *no* default
482     // behavior.  This is because we don't know what the callback is going
483     // to do.  For instance, the driver could reopen the target to a
484     // different device in a multi-path scenario.
485     //
486     if (pThis->m_EvtQueryRemove.m_Method != NULL) {
487         status = pThis->m_EvtQueryRemove.Invoke(
488             pThis->GetHandle());
489     }
490     else {
491         DoTraceLevelMessage(
492             pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
493             "WDFIOTARGET %p: query remove, default action (close for QR)",
494             pThis->GetObjectHandle());
495 
496         //
497         // No callback, close it down conditionally.
498         //
499         pThis->Close(FxIoTargetRemoteCloseReasonQueryRemove);
500     }
501 
502     if (!NT_SUCCESS(status)) {
503         bStatus = FALSE;
504     }
505 
506 exit:
507 
508     pThis->RELEASE(m_RemoteTarget);
509 
510     return bStatus;
511 }
512 
513 VOID
514 __stdcall
OnRemoveComplete(_In_ WUDF_TARGET_CONTEXT RegistrationID)515 FxIoTargetRemoteNotificationCallback::OnRemoveComplete(
516     _In_ WUDF_TARGET_CONTEXT RegistrationID
517     )
518 {
519     PFX_DRIVER_GLOBALS pFxDriverGlobals;
520     FxIoTargetRemote* pThis;
521 
522     pThis = m_RemoteTarget;
523 
524     //
525     // In one of these callbacks, the driver may decide to delete the target.
526     // If that is the case, we need to be able to return and deref the object until
527     // we are done.
528     //
529     pThis->ADDREF(m_RemoteTarget);
530 
531     pFxDriverGlobals = pThis->GetDriverGlobals();
532 
533     if (GetRegistrationId() != RegistrationID) {
534         DoTraceLevelMessage(
535             pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGIOTARGET,
536                     "RemoveComplete callback was for an old registration, ignoring.");
537 
538         goto exit;
539     }
540 
541     DoTraceLevelMessage(
542         pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
543         "WDFIOTARGET %p: remove complete notification", pThis->GetObjectHandle());
544 
545     //
546     // The device was surprise removed, close it for good if the driver has
547     // no override.
548     //
549     if (pThis->m_EvtRemoveComplete.m_Method != NULL) {
550         pThis->m_EvtRemoveComplete.Invoke(pThis->GetHandle());
551     }
552     else {
553         DoTraceLevelMessage(
554             pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
555             "WDFIOTARGET %p: remove complete, default action (close)",
556             pThis->GetObjectHandle());
557 
558         //
559         // The device is now gone for good.  Close down the target for good.
560         //
561         pThis->Close(FxIoTargetRemoteCloseReasonPlainClose);
562     }
563 
564 exit:
565 
566     pThis->RELEASE(m_RemoteTarget);
567 }
568 
569 VOID
570 __stdcall
OnRemoveCanceled(_In_ WUDF_TARGET_CONTEXT RegistrationID)571 FxIoTargetRemoteNotificationCallback::OnRemoveCanceled(
572     _In_ WUDF_TARGET_CONTEXT RegistrationID
573     )
574 {
575     PFX_DRIVER_GLOBALS pFxDriverGlobals;
576     FxIoTargetRemote* pThis;
577     NTSTATUS status;
578 
579     pThis = m_RemoteTarget;
580 
581     //
582     // In one of these callbacks, the driver may decide to delete the target.
583     // If that is the case, we need to be able to return and deref the object until
584     // we are done.
585     //
586     pThis->ADDREF(m_RemoteTarget);
587 
588     pFxDriverGlobals = pThis->GetDriverGlobals();
589     status = STATUS_SUCCESS;
590 
591     if (GetRegistrationId() != RegistrationID) {
592         DoTraceLevelMessage(
593             pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGIOTARGET,
594                     "RemoveCanceled callback was for an old registration, ignoring.");
595 
596         goto exit;
597     }
598 
599     DoTraceLevelMessage(
600         pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
601         "WDFIOTARGET %p: remove canceled notification", pThis->GetObjectHandle());
602 
603     if (pThis->m_EvtRemoveCanceled.m_Method != NULL) {
604         pThis->m_EvtRemoveCanceled.Invoke(pThis->GetHandle());
605     }
606     else {
607         WDF_IO_TARGET_OPEN_PARAMS params;
608 
609         DoTraceLevelMessage(
610             pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
611             "WDFIOTARGET %p: remove canceled, default action (reopen)",
612             pThis->GetObjectHandle());
613 
614         WDF_IO_TARGET_OPEN_PARAMS_INIT_REOPEN(&params);
615 
616         //
617         // Attempt to reopen the target with stored settings
618         //
619         status = pThis->Open(&params);
620 
621 
622 
623 
624 
625 
626 
627         UNREFERENCED_PARAMETER(status);
628     }
629 
630 exit:
631 
632     pThis->RELEASE(m_RemoteTarget);
633 }
634 
635 VOID
636 __stdcall
OnCustomEvent(_In_ WUDF_TARGET_CONTEXT RegistrationID,_In_ REFGUID Event,_In_reads_bytes_ (DataSize)BYTE * Data,_In_ DWORD DataSize,_In_ DWORD NameBufferOffset)637 FxIoTargetRemoteNotificationCallback::OnCustomEvent(
638     _In_ WUDF_TARGET_CONTEXT  RegistrationID,
639     _In_ REFGUID Event,
640     _In_reads_bytes_(DataSize) BYTE * Data,
641     _In_ DWORD DataSize,
642     _In_ DWORD NameBufferOffset
643     )
644 {
645     UNREFERENCED_PARAMETER(RegistrationID);
646     UNREFERENCED_PARAMETER(Event);
647     UNREFERENCED_PARAMETER(Data);
648     UNREFERENCED_PARAMETER(DataSize);
649     UNREFERENCED_PARAMETER(NameBufferOffset);
650 
651     //
652     // UMDF 2.0 doesn't yet support custom event. Ignore the event.
653     //
654     DO_NOTHING();
655 
656     return;
657 }
658 
659