1 /*++
2 
3 Copyright (c) Microsoft. All rights reserved.
4 
5 Module Name:
6 
7     FxRequestBufferKm.cpp
8 
9 Abstract:
10 
11     This module implements a memory union object
12 
13 Author:
14 
15 
16 
17 Environment:
18 
19     Kernel mode only
20 
21 Revision History:
22 
23 --*/
24 
25 #include "fxsupportpch.hpp"
26 
27 extern "C" {
28 // #include "FxRequestBufferKm.tmh"
29 }
30 
31 _Must_inspect_result_
32 NTSTATUS
33 FxRequestBuffer::GetOrAllocateMdl(
34     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
35     __deref_out_opt PMDL*   Mdl,
36     __inout PMDL*           MdlToFree,
37     __inout PBOOLEAN        UnlockWhenFreed,
38     __in LOCK_OPERATION     Operation,
39     __in BOOLEAN            ReuseMdl,
40     __inout_opt size_t*     SizeOfMdl
41     )
42 /*++
43 
44 Routine Description:
45 
46     This function attempts to reuse the passed-in MDL (if any) or allocates
47     a new MDL if reuse flag isn't passed-in or if the existing MDL isn't
48     big enough.
49 
50 Arguments:
51 
52 Return Value:
53     FxDriverGlobals - Driver globals
54 
55     Mdl - on return it contains the MDL allocated/reused
56 
57     MdlToFree - pointer to any MDL
58                   * to be reused, if the size is <= current size, or
59                   * freed and set to newly allocated MDL
60 
61     UnlockWhenFreed - whether to unlock pages when freeing MDL
62                       (if FALSE, MDL may represent just MDL buffer but the pages
63                       might have already been unlocked)
64 
65     Operation - Operation to pass to MmLockPages
66 
67     ReuseMdl - whether to reuse *MdlToFree
68                Please note that this can be FALSE even when MDL is supplied
69 
70     SizeOfMdl - on input contains size of *MdlToFree,
71                 on return contains size of *Mdl
72 
73 Remarks:
74 
75     *MdlToFree is modified only when this function frees the passed in MDL
76     Otherwise it leaves it untouched. Caller is responsible for storing
77     properly initialized value and/or freeing what's stored in the value.
78 
79   --*/
80 {
81     PVOID pBuf;
82     NTSTATUS status;
83     ULONG length;
84     BOOLEAN oldUnlockValue;
85 
86     pBuf = NULL;
87 
88     oldUnlockValue =  *UnlockWhenFreed;
89 
90     //
91     // Format functions that use this helper call
92     // FxRequestBase::ValidateTarget which calls ContextReleaseAndRestore
93     // which unlocks any locked pages.
94     //
95     // Hence pages must already be unlocked now. Let's assert that.
96     //
97     // This condition needs to be true since we unconditionally set
98     // *UnlockWhenFreed to FALSE just below.
99     //
100     ASSERT (oldUnlockValue == FALSE);
101 
102     *UnlockWhenFreed = FALSE;
103 
104     //
105     // Even if ReuseMdl is not true, SizeOfMdl may be supplied to store
106     // the size of allocated MDL to be used later
107     //
108     ASSERT(ReuseMdl ? (SizeOfMdl != NULL && *MdlToFree != NULL) : TRUE);
109 
110     switch (DataType) {
111     case FxRequestBufferUnspecified:
112         *Mdl = NULL;
113         //
114         // We should not set *MdlToFree to NULL as *MdlToFree might have a valid
115         // MDL which we should not overwrite with NULL without freeing it
116         //
117         return STATUS_SUCCESS;
118 
119     case FxRequestBufferMemory:
120         if (u.Memory.Offsets != NULL) {
121             pBuf = WDF_PTR_ADD_OFFSET(u.Memory.Memory->GetBuffer(),
122                                       u.Memory.Offsets->BufferOffset);
123         }
124         else {
125             pBuf = u.Memory.Memory->GetBuffer();
126         }
127         //   ||   ||
128         //   \/   \/  fall through
129 
130     case FxRequestBufferBuffer:
131         if (pBuf == NULL) {
132             pBuf = u.Buffer.Buffer;
133         }
134 
135         length = GetBufferLength();
136         status = GetOrAllocateMdlWorker(FxDriverGlobals,
137                                Mdl,
138                                &ReuseMdl,
139                                length,
140                                pBuf,
141                                SizeOfMdl,
142                                oldUnlockValue,
143                                MdlToFree
144                                );
145         if (!NT_SUCCESS(status)) {
146             DoTraceLevelMessage(
147                 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGAPIERROR,
148                 "Couldn't allocate memory for MDL of length 0x%x %!STATUS!", length, status);
149             return status;
150         }
151 
152         //
153         // If we are reusing the MDL we need to initialize it with current
154         // buffer.
155         //
156         if (ReuseMdl == TRUE) {
157             Mx::MxInitializeMdl(*Mdl, pBuf, length);
158         }
159 
160         status = FxProbeAndLockWithAccess(*Mdl, KernelMode, Operation);
161 
162         if (!NT_SUCCESS(status)) {
163             DoTraceLevelMessage(
164                 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGAPIERROR,
165                 "Couldn't lock pages for MDL 0x%p %!STATUS!", *Mdl, status);
166 
167             //
168             // Free MDL only if it was not reused.
169             //
170             if (ReuseMdl == FALSE) {
171                 FxMdlFree(FxDriverGlobals, *Mdl);
172             }
173 
174             *Mdl = NULL;
175             return status;
176         }
177 
178         *UnlockWhenFreed = TRUE;
179         *MdlToFree = *Mdl;
180 
181         return STATUS_SUCCESS;
182 
183     case FxRequestBufferMdl:
184         *Mdl = u.Mdl.Mdl;
185         //
186         // We should not set *MdlToFree to NULL as *MdlToFree might have a valid
187         // MDL which we should not overwrite with NULL without freeing it
188         //
189         return STATUS_SUCCESS;
190 
191     case FxRequestBufferReferencedMdl:
192         if (u.RefMdl.Offsets == NULL ||
193             (u.RefMdl.Offsets->BufferOffset == 0 && u.RefMdl.Offsets->BufferLength == 0)) {
194             *Mdl = u.RefMdl.Mdl;
195             //
196             // We should not set *MdlToFree to NULL as *MdlToFree might have a valid
197             // MDL which we should not overwrite with NULL without freeing it
198             //
199         }
200         else {
201             //
202             // Do not use MmGetSystemAddressForMdlSafe because StartVa could be
203             // in UM while MappedVa (obviously) is in KM.   Since
204             // IoBuildPartial Mdl basically uses
205             // pBuf - MmGetMdlVirtualAddress(SrcMdl) to compute offset, if one
206             // VA is in UM (e.g. MmGetMdlVirtualAddress(SrcMdl)), you get the
207             // (drastically) wrong offset.
208             //
209             pBuf =  Mx::MxGetMdlVirtualAddress(u.RefMdl.Mdl);
210             ASSERT(pBuf != NULL);
211 
212             pBuf = WDF_PTR_ADD_OFFSET(pBuf, u.RefMdl.Offsets->BufferOffset);
213 
214             //
215             // GetBufferLength will compute the correct length with the given offsets
216             //
217             length = GetBufferLength();
218             status = GetOrAllocateMdlWorker(FxDriverGlobals,
219                                    Mdl,
220                                    &ReuseMdl,
221                                    length,
222                                    pBuf,
223                                    SizeOfMdl,
224                                    oldUnlockValue,
225                                    MdlToFree
226                                    );
227             if (!NT_SUCCESS(status)) {
228                 DoTraceLevelMessage(
229                     FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGAPIERROR,
230                     "Couldn't allocate memory for MDL of length 0x%x %!STATUS!", length, status);
231                 return status;
232             }
233 
234             Mx::MxBuildPartialMdl(
235                 u.RefMdl.Mdl,
236                 *Mdl,
237                 pBuf,
238                 length
239                 );
240 
241             *MdlToFree = *Mdl;
242         }
243 
244         return STATUS_SUCCESS;
245 
246     default:
247         *Mdl = NULL;
248         //
249         // We should not set *MdlToFree to NULL as *MdlToFree might have a valid
250         // MDL which we should not overwrite with NULL without freeing it
251         //
252         return STATUS_INVALID_PARAMETER;
253     }
254 }
255 
256 VOID
257 FxRequestBuffer::SetMemory(
258     __in IFxMemory* Memory,
259     __in PWDFMEMORY_OFFSET Offsets
260     )
261 {
262     PMDL pMdl;
263 
264     pMdl = Memory->GetMdl();
265     if (pMdl != NULL) {
266         DataType = FxRequestBufferReferencedMdl;
267         u.RefMdl.Memory = Memory;
268         u.RefMdl.Offsets = Offsets;
269         u.RefMdl.Mdl = pMdl;
270     }
271     else {
272         DataType = FxRequestBufferMemory;
273         u.Memory.Memory = Memory;
274         u.Memory.Offsets = Offsets;
275     }
276 }
277 
278