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
GetOrAllocateMdl(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__deref_out_opt PMDL * Mdl,__inout PMDL * MdlToFree,__inout PBOOLEAN UnlockWhenFreed,__in LOCK_OPERATION Operation,__in BOOLEAN ReuseMdl,__inout_opt size_t * SizeOfMdl)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
SetMemory(__in IFxMemory * Memory,__in PWDFMEMORY_OFFSET Offsets)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