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