1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxIoTargetKm.cpp
8 
9 Abstract:
10 
11     This module implements the IO Target APIs
12 
13 Author:
14 
15 Environment:
16 
17     kernel mode only
18 
19 Revision History:
20 
21 --*/
22 
23 
24 #include "../../fxtargetsshared.hpp"
25 
26 extern "C" {
27 #if defined(EVENT_TRACING)
28 #include "FxIoTargetKm.tmh"
29 #endif
30 }
31 
32 _Must_inspect_result_
33 NTSTATUS
34 FxIoTarget::FormatIoRequest(
35     __inout FxRequestBase* Request,
36     __in UCHAR MajorCode,
37     __in FxRequestBuffer* IoBuffer,
38     __in_opt PLONGLONG DeviceOffset,
39     _In_opt_ FxFileObject* FileObject
40     )
41 {
42     FxIoContext* pContext;
43     PVOID pBuffer;
44     NTSTATUS status;
45     ULONG ioLength;
46     BOOLEAN freeSysBuf;
47     BOOLEAN setBufferAndLength;
48     FxIrp* irp;
49 
50     UNREFERENCED_PARAMETER(FileObject);
51 
52     ASSERT(MajorCode == IRP_MJ_WRITE || MajorCode == IRP_MJ_READ);
53 
54     freeSysBuf = FALSE;
55     pBuffer = NULL;
56 
57     status = Request->ValidateTarget(this);
58     if (!NT_SUCCESS(status)) {
59         return status;
60     }
61 
62     if (Request->HasContextType(FX_RCT_IO)) {
63         pContext = (FxIoContext*) Request->GetContext();
64     }
65     else {
66         pContext = new(GetDriverGlobals()) FxIoContext();
67         if (pContext == NULL) {
68             DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
69                                 "could not allocate context for request");
70 
71             return STATUS_INSUFFICIENT_RESOURCES;
72         }
73 
74         //
75         // Since we can error out and return, remember the allocation before
76         // we do anything so we can free it later.
77         //
78         Request->SetContext(pContext);
79     }
80 
81     //
82     // Save away any references to IFxMemory pointers that are passed
83     //
84     pContext->StoreAndReferenceMemory(IoBuffer);
85 
86     irp = Request->GetSubmitFxIrp();
87     irp->ClearNextStackLocation();
88 
89     CopyFileObjectAndFlags(Request);
90 
91     //
92     // Note that by convention "Set" methods of FxIrp apply to next stack
93     // location unless specified otherwise in the name.
94     //
95     irp->SetMajorFunction(MajorCode);
96     pContext->m_MajorFunction = MajorCode;
97 
98     //
99     // Anytime we return here and we allocated the context above, the context
100     // will be freed when the FxRequest is freed or reformatted.
101     //
102 
103     ioLength = IoBuffer->GetBufferLength();
104 
105     pContext->CaptureState(irp);
106 
107     switch (m_TargetIoType) {
108     case WdfDeviceIoBuffered:
109         irp->SetUserBuffer(NULL);
110 
111         if (ioLength != 0) {
112 
113 
114             if ((pContext->m_BufferToFreeLength >= ioLength) &&
115                 (pContext->m_BufferToFree != NULL)) {
116                     irp->SetSystemBuffer(pContext->m_BufferToFree);
117                     setBufferAndLength = FALSE;
118             }
119             else {
120                 irp->SetSystemBuffer(FxPoolAllocate(GetDriverGlobals(),
121                                    NonPagedPool,
122                                    ioLength));
123                 if (irp->GetSystemBuffer() == NULL) {
124                     DoTraceLevelMessage(
125                         GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
126                         "Could not allocate common buffer");
127 
128                     status = STATUS_INSUFFICIENT_RESOURCES;
129                     break;
130                 }
131 
132                 setBufferAndLength = TRUE;
133                 freeSysBuf = TRUE;
134             }
135 
136             status = IoBuffer->GetBuffer(&pBuffer);
137 
138             if (!NT_SUCCESS(status)) {
139                 DoTraceLevelMessage(
140                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
141                     "Could not retrieve io buffer, %!STATUS!", status);
142                 break;
143             }
144 
145             //
146             // If its a write, copy into the double buffer now, otherwise,
147             // no copy into the buffer is needed for a read.
148             //
149             if (MajorCode == IRP_MJ_WRITE) {
150                 if (pBuffer != NULL) {
151                     RtlCopyMemory(irp->GetSystemBuffer(),
152                                   pBuffer,
153                                   ioLength);
154                 }
155             }
156             else {
157                 irp->SetUserBuffer(pBuffer);
158             }
159 
160             //
161             // On reads, copy back to the double buffer after the read has
162             // completed.
163             //
164             if (setBufferAndLength) {
165                 pContext->SetBufferAndLength(irp->GetSystemBuffer(),
166                                     ioLength,
167                                     (MajorCode == IRP_MJ_READ) ? TRUE : FALSE);
168 
169                 freeSysBuf = FALSE; // FxIoContext will free the buffer.
170             }
171             else {
172                 pContext->m_CopyBackToBuffer = MajorCode == IRP_MJ_READ  ?
173                                                                 TRUE : FALSE;
174             }
175         }
176         else {
177             //
178             // This field was captured and will be restored by the context
179             // later.
180             //
181             irp->SetSystemBuffer(NULL);
182         }
183         break;
184     case WdfDeviceIoDirect:
185     {
186         BOOLEAN reuseMdl;
187 
188         reuseMdl = FALSE;
189 
190         if (pContext->m_MdlToFree != NULL) {
191             reuseMdl = TRUE;
192         }
193 
194         status = IoBuffer->GetOrAllocateMdl(
195             GetDriverGlobals(),
196             irp->GetMdlAddressPointer(),
197             &pContext->m_MdlToFree,
198             &pContext->m_UnlockPages,
199             (MajorCode == IRP_MJ_READ) ? IoWriteAccess : IoReadAccess,
200             reuseMdl,
201             &pContext->m_MdlToFreeSize
202             );
203 
204         if (!NT_SUCCESS(status)) {
205             DoTraceLevelMessage(
206                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
207                 "Could not retrieve io buffer as a PMDL, %!STATUS!",
208                 status);
209             break;
210         }
211         break;
212     }
213     case WdfDeviceIoNeither:
214         //
215         // Neither MDL nor buffered
216         //
217         status = IoBuffer->GetBuffer(&pBuffer);
218 
219         if (NT_SUCCESS(status)) {
220             irp->SetUserBuffer(pBuffer);
221         }
222         else {
223             DoTraceLevelMessage(
224                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
225                 "Could not retrieve io buffer as a PVOID, %!STATUS!",
226                 status);
227         }
228         break;
229 
230     case WdfDeviceIoUndefined:
231     default:
232         status = STATUS_INVALID_DEVICE_STATE;
233         DoTraceLevelMessage(
234             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
235             "Trying to format closed WDFIOTARGET %p, %!STATUS!",
236             GetHandle(), status);
237         break;
238     }
239 
240     //
241     // We are assuming  the read and write parts of the Parameters union
242     // are at the same offset.  If this is FALSE, WDFCASSERT will not allow
243     // this file to compile, so keep these WDFCASSERTs here as long as the
244     // assumption is being made.
245     //
246     WDFCASSERT(FIELD_OFFSET(IO_STACK_LOCATION, Parameters.Write.ByteOffset)
247                ==
248                FIELD_OFFSET(IO_STACK_LOCATION, Parameters.Read.ByteOffset));
249 
250     WDFCASSERT(FIELD_OFFSET(IO_STACK_LOCATION, Parameters.Write.Length)
251                ==
252                FIELD_OFFSET(IO_STACK_LOCATION, Parameters.Read.Length));
253 
254     if (NT_SUCCESS(status)) {
255 
256         irp->SetNextParameterWriteLength(ioLength);
257         if (DeviceOffset != NULL) {
258             irp->SetNextParameterWriteByteOffsetQuadPart(*DeviceOffset);
259         }
260         else {
261             irp->SetNextParameterWriteByteOffsetQuadPart(0);
262         }
263 
264         Request->VerifierSetFormatted();
265     }
266     else {
267         if (freeSysBuf) {
268             FxPoolFree(irp->GetSystemBuffer());
269             irp->SetSystemBuffer(NULL);
270         }
271 
272         Request->ContextReleaseAndRestore();
273     }
274 
275     return status;
276 }
277 
278 _Must_inspect_result_
279 NTSTATUS
280 FxIoTarget::FormatIoctlRequest(
281     __in FxRequestBase* Request,
282     __in ULONG Ioctl,
283     __in BOOLEAN Internal,
284     __in FxRequestBuffer* InputBuffer,
285     __in FxRequestBuffer* OutputBuffer,
286     _In_opt_ FxFileObject* FileObject
287     )
288 {
289     FxIoContext* pContext;
290     NTSTATUS status;
291     PVOID pBuffer;
292     ULONG inLength, outLength;
293     BOOLEAN freeSysBuf;
294     BOOLEAN setBufferAndLength;
295     FxIrp* irp;
296 
297     UNREFERENCED_PARAMETER(FileObject);
298 
299     irp = Request->GetSubmitFxIrp();
300     freeSysBuf = FALSE;
301 
302     status = Request->ValidateTarget(this);
303     if (!NT_SUCCESS(status)) {
304         return status;
305     }
306 
307     if (Request->HasContextType(FX_RCT_IO)) {
308         pContext = (FxIoContext*) Request->GetContext();
309     }
310     else {
311         pContext = new(GetDriverGlobals()) FxIoContext();
312         if (pContext == NULL) {
313             DoTraceLevelMessage(
314                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
315                 "Could not allocate context for request");
316 
317             return STATUS_INSUFFICIENT_RESOURCES;
318         }
319 
320         Request->SetContext(pContext);
321     }
322 
323     pContext->CaptureState(irp);
324 
325     irp->ClearNextStackLocation();
326 
327     //
328     // Save away any references to IFxMemory pointers that are passed
329     //
330     pContext->StoreAndReferenceMemory(InputBuffer);
331     pContext->StoreAndReferenceOtherMemory(OutputBuffer);
332 
333     UCHAR majorFunction;
334     if (Internal) {
335         majorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
336     }
337     else {
338         majorFunction = IRP_MJ_DEVICE_CONTROL;
339     }
340 
341     irp->SetMajorFunction(majorFunction);
342 
343     pContext->m_MajorFunction = majorFunction;
344 
345     CopyFileObjectAndFlags(Request);
346 
347     inLength = InputBuffer->GetBufferLength();
348     outLength = OutputBuffer->GetBufferLength();
349 
350     irp->SetParameterIoctlCode(Ioctl);
351     irp->SetParameterIoctlInputBufferLength(inLength);
352     irp->SetParameterIoctlOutputBufferLength(outLength);
353 
354 
355     //
356     // Anytime we return here and we allocated the context above, the context
357     // will be freed when the FxRequest is freed or reformatted.
358     //
359     switch (METHOD_FROM_CTL_CODE(Ioctl)) {
360     case METHOD_BUFFERED:
361 
362         if (inLength != 0 || outLength != 0) {
363             ULONG allocationLength;
364 
365             allocationLength = (inLength > outLength ? inLength : outLength);
366 
367             if ((pContext->m_BufferToFreeLength >= allocationLength) &&
368                 (pContext->m_BufferToFree != NULL)) {
369                     irp->SetSystemBuffer(pContext->m_BufferToFree);
370                     setBufferAndLength = FALSE;
371             }
372             else {
373                 irp->SetSystemBuffer(FxPoolAllocate(GetDriverGlobals(),
374                                    NonPagedPool,
375                                    allocationLength));
376                 if (irp->GetSystemBuffer() == NULL) {
377                     DoTraceLevelMessage(
378                         GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
379                         "Could not allocate common buffer");
380                     status = STATUS_INSUFFICIENT_RESOURCES;
381                     break;
382                 }
383                 setBufferAndLength = TRUE;
384                 freeSysBuf = TRUE;
385             }
386 
387             status = InputBuffer->GetBuffer(&pBuffer);
388             if (!NT_SUCCESS(status)) {
389                 DoTraceLevelMessage(
390                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
391                     "Could not retrieve input buffer, %!STATUS!",
392                     status);
393                 break;
394             }
395 
396             if (pBuffer != NULL) {
397                 RtlCopyMemory(irp->GetSystemBuffer(),
398                               pBuffer,
399                               inLength);
400             }
401 
402             status = OutputBuffer->GetBuffer(&pBuffer);
403             if (!NT_SUCCESS(status)) {
404                 DoTraceLevelMessage(
405                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
406                     "Could not retrieve output buffer, %!STATUS!",
407                     status);
408                 break;
409             }
410 
411             irp->SetUserBuffer(pBuffer);
412             if (setBufferAndLength) {
413                 pContext->SetBufferAndLength(irp->GetSystemBuffer(),
414                                              allocationLength,
415                                              outLength > 0  ? TRUE : FALSE);
416                 freeSysBuf = FALSE; // FxIoContext will free the buffer.
417             } else {
418                 pContext->m_CopyBackToBuffer = outLength > 0  ? TRUE : FALSE;
419             }
420 
421         }
422         else {
423             //
424             // These fields were captured and will be restored by the context
425             // later.
426             //
427             irp->SetUserBuffer(NULL);
428             irp->SetSystemBuffer(NULL);
429         }
430 
431         break;
432 
433     case METHOD_DIRECT_TO_HARDWARE:   // METHOD_IN_DIRECT
434     case METHOD_DIRECT_FROM_HARDWARE: // METHOD_OUT_DIRECT
435     {
436         BOOLEAN reuseMdl;
437 
438         reuseMdl = FALSE;
439 
440         status = InputBuffer->GetBuffer(&pBuffer);
441         if (!NT_SUCCESS(status)) {
442             DoTraceLevelMessage(
443                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
444                 "Could not retrieve input buffer as a PVOID, %!STATUS!",
445                 status);
446             break;
447         }
448 
449         irp->SetSystemBuffer(pBuffer);
450 
451         //
452         // NOTE: There is no need to compare the operation type since that
453         // applies only to the Pages locked in memory and not the MDL data
454         // structure itself per se.
455         // Also, note that if the size of the Outbuf need not be equal to the
456         // size of the MdlToFree as long as the number of page entries match.
457         //
458         if (pContext->m_MdlToFree != NULL) {
459             reuseMdl = TRUE;
460         }
461 
462         status = OutputBuffer->GetOrAllocateMdl(
463             GetDriverGlobals(),
464             irp->GetMdlAddressPointer(),
465             &pContext->m_MdlToFree,
466             &pContext->m_UnlockPages,
467             (METHOD_FROM_CTL_CODE(Ioctl) == METHOD_DIRECT_TO_HARDWARE) ? IoReadAccess : IoWriteAccess,
468             reuseMdl,
469             &pContext->m_MdlToFreeSize
470             );
471 
472         if (!NT_SUCCESS(status)) {
473             DoTraceLevelMessage(
474                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
475                 "Could not retrieve output buffer as a PMDL, %!STATUS!",
476                 status);
477             break;
478         }
479         break;
480     }
481 
482     case METHOD_NEITHER:
483         status = OutputBuffer->GetBuffer(&pBuffer);
484 
485         if (!NT_SUCCESS(status)) {
486             DoTraceLevelMessage(
487                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
488                 "Could not retrieve output buffer as a PVOID, %!STATUS!",
489                 status);
490             break;
491         }
492 
493         irp->SetUserBuffer(pBuffer);
494 
495         status = InputBuffer->GetBuffer(&pBuffer);
496 
497         if (!NT_SUCCESS(status)) {
498             DoTraceLevelMessage(
499                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
500                 "Could not retrieve input buffer as a PVOID, %!STATUS!",
501                 status);
502 
503             break;
504         }
505 
506         irp->SetParameterIoctlType3InputBuffer(pBuffer);
507         break;
508     }
509 
510     if (NT_SUCCESS(status)) {
511         Request->VerifierSetFormatted();
512     }
513     else {
514         if (freeSysBuf) {
515             FxPoolFree(irp->GetSystemBuffer());
516             irp->SetSystemBuffer(NULL);
517         }
518 
519         Request->ContextReleaseAndRestore();
520     }
521 
522     return status;
523 }
524 
525