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